using System;
using System.Diagnostics;
using System.IO;
using System.Text;

using i16 = System.Int16;
using u8 = System.Byte;
using u16 = System.UInt16;
using u32 = System.UInt32;

/*
** The yDbMask datatype for the bitmask of all attached databases.
*/
#if SQLITE_MAX_ATTACHED//>30
//  typedef sqlite3_uint64 yDbMask;
using yDbMask = System.Int64;
#else

//  typedef unsigned int yDbMask;
using yDbMask = System.Int32;

#endif

namespace Community.CsharpSqlite
{
	public partial class Sqlite3
	{
		/*
		** 2001 September 15
		**
		** The author disclaims copyright to this source code.  In place of
		** a legal notice, here is a blessing:
		**
		**    May you do good and not evil.
		**    May you find forgiveness for yourself and forgive others.
		**    May you share freely, never taking more than you give.
		**
		*************************************************************************
		** This file contains C code routines that are called by the SQLite parser
		** when syntax rules are reduced.  The routines in this file handle the
		** following kinds of SQL syntax:
		**
		**     CREATE TABLE
		**     DROP TABLE
		**     CREATE INDEX
		**     DROP INDEX
		**     creating ID lists
		**     BEGIN TRANSACTION
		**     COMMIT
		**     ROLLBACK
		*************************************************************************
		**  Included in SQLite3 port to C#-SQLite;  2008 Noah B Hart
		**  C#-SQLite is an independent reimplementation of the SQLite software library
		**
		**  SQLITE_SOURCE_ID: 2011-06-23 19:49:22 4374b7e83ea0a3fbc3691f9c0c936272862f32f2
		**
		*************************************************************************
		*/
		//#include "sqliteInt.h"

		/*
		** This routine is called when a new SQL statement is beginning to
		** be parsed.  Initialize the pParse structure as needed.
		*/

		private static void sqlite3BeginParse(Parse pParse, int explainFlag)
		{
			pParse.explain = (byte)explainFlag;
			pParse.nVar = 0;
		}

#if !SQLITE_OMIT_SHARED_CACHE
/*
** The TableLock structure is only used by the sqlite3TableLock() and
** codeTableLocks() functions.
*/
//struct TableLock {
//  int iDb;             /* The database containing the table to be locked */
//  int iTab;            /* The root page of the table to be locked */
//  u8 isWriteLock;      /* True for write lock.  False for a read lock */
//  string zName;   /* Name of the table */
//};

public class TableLock
{
public int iDb;         /* The database containing the table to be locked */
public int iTab;        /* The root page of the table to be locked */
public u8 isWriteLock;  /* True for write lock.  False for a read lock */
public string zName;    /* Name of the table */
}
/*
** Record the fact that we want to lock a table at run-time.
**
** The table to be locked has root page iTab and is found in database iDb.
** A read or a write lock can be taken depending on isWritelock.
**
** This routine just records the fact that the lock is desired.  The
** code to make the lock occur is generated by a later call to
** codeTableLocks() which occurs during sqlite3FinishCoding().
*/
void sqlite3TableLock(
  Parse *pParse,     /* Parsing context */
  int iDb,           /* Index of the database containing the table to lock */
  int iTab,          /* Root page number of the table to be locked */
  u8 isWriteLock,    /* True for a write lock */
  string zName  /* Name of the table to be locked */
){
  Parse *pToplevel = sqlite3ParseToplevel(pParse);
  int i;
  int nBytes;
  TableLock *p;
  Debug.Assert( iDb>=0 );

  for(i=0; i<pToplevel->nTableLock; i++){
    p = pToplevel->aTableLock[i];
    if( p->iDb==iDb && p->iTab==iTab ){
      p->isWriteLock = (p->isWriteLock || isWriteLock);
      return;
    }
  }

  nBytes = sizeof(vtableLock) * (pToplevel->nTableLock+1);
  pToplevel->aTableLock =
      sqlite3DbReallocOrFree(pToplevel->db, pToplevel->aTableLock, nBytes);
  if( pToplevel->aTableLock ){
    p = pToplevel->aTableLock[pToplevel->nTableLock++];
    p->iDb = iDb;
    p->iTab = iTab;
    p->isWriteLock = isWriteLock;
    p->zName = zName;
  }else{
    pToplevel->nTableLock = 0;
    pToplevel->db->mallocFailed = 1;
  }
}

/*
** Code an OP_TableLock instruction for each table locked by the
** statement (configured by calls to sqlite3TableLock()).
*/
static void codeTableLocks( Parse pParse )
{
int i;
Vdbe pVdbe;

pVdbe = sqlite3GetVdbe( pParse );
Debug.Assert( pVdbe != null ); /* sqlite3GetVdbe cannot fail: VDBE already allocated */

for ( i = 0 ; i < pParse.nTableLock ; i++ )
{
TableLock p = pParse.aTableLock[i];
int p1 = p.iDb;
sqlite3VdbeAddOp4( pVdbe, OP_TableLock, p1, p.iTab, p.isWriteLock,
p.zName, P4_STATIC );
}
}
#else

		//  #define codeTableLocks(x)
		private static void codeTableLocks(Parse pParse)
		{
		}

#endif

		/*
** This routine is called after a single SQL statement has been
** parsed and a VDBE program to execute that statement has been
** prepared.  This routine puts the finishing touches on the
** VDBE program and resets the pParse structure for the next
** parse.
**
** Note that if an error occurred, it might be the case that
** no VDBE code was generated.
*/

		private static void sqlite3FinishCoding(Parse pParse)
		{
			sqlite3 db;
			Vdbe v;

			db = pParse.db;
			//      if ( db.mallocFailed != 0 ) return;
			if (pParse.nested != 0)
				return;
			if (pParse.nErr != 0)
				return;

			/* Begin by generating some termination code at the end of the
			** vdbe program
			*/
			v = sqlite3GetVdbe(pParse);
			Debug.Assert(0 == pParse.isMultiWrite
#if SQLITE_DEBUG
 || sqlite3VdbeAssertMayAbort(v, pParse.mayAbort) != 0
#endif
);
			if (v != null)
			{
				sqlite3VdbeAddOp0(v, OP_Halt);

				/* The cookie mask contains one bit for each database file open.
				** (Bit 0 is for main, bit 1 is for temp, and so forth.)  Bits are
				** set for each database that is used.  Generate code to start a
				** transaction on each used database and to verify the schema cookie
				** on each used database.
				*/
				if (pParse.cookieGoto > 0)
				{
					u32 mask;
					int iDb;
					sqlite3VdbeJumpHere(v, pParse.cookieGoto - 1);
					for (iDb = 0, mask = 1; iDb < db.nDb; mask <<= 1, iDb++)
					{
						if ((mask & pParse.cookieMask) == 0)
							continue;
						sqlite3VdbeUsesBtree(v, iDb);
						sqlite3VdbeAddOp2(v, OP_Transaction, iDb, (mask & pParse.writeMask) != 0);
						if (db.init.busy == 0)
						{
							Debug.Assert(sqlite3SchemaMutexHeld(db, iDb, null));
							sqlite3VdbeAddOp3(v, OP_VerifyCookie,
											  iDb, pParse.cookieValue[iDb],
											  (int)db.aDb[iDb].pSchema.iGeneration);
						}
					}
#if !SQLITE_OMIT_VIRTUALTABLE
					{
						int i;
						for (i = 0; i < pParse.nVtabLock; i++)
						{
							VTable vtab = sqlite3GetVTable(db, pParse.apVtabLock[i]);
							sqlite3VdbeAddOp4(v, OP_VBegin, 0, 0, 0, vtab, P4_VTAB);
						}
						pParse.nVtabLock = 0;
					}
#endif

					/* Once all the cookies have been verified and transactions opened,
** obtain the required table-locks. This is a no-op unless the
** shared-cache feature is enabled.
*/
					codeTableLocks(pParse);

					/* Initialize any AUTOINCREMENT data structures required.
					*/
					sqlite3AutoincrementBegin(pParse);

					/* Finally, jump back to the beginning of the executable code. */
					sqlite3VdbeAddOp2(v, OP_Goto, 0, pParse.cookieGoto);
				}
			}

			/* Get the VDBE program ready for execution
			*/
			if (v != null && ALWAYS(pParse.nErr == 0) /* && 0 == db.mallocFailed */ )
			{
#if  SQLITE_DEBUG && !SQLITE_WINRT
				TextWriter trace = (db.flags & SQLITE_VdbeTrace) != 0 ? Console.Out : null;
				sqlite3VdbeTrace(v, trace);
#endif
				Debug.Assert(pParse.iCacheLevel == 0);  /* Disables and re-enables match */
				/* A minimum of one cursor is required if autoincrement is used
				*  See ticket [a696379c1f08866] */
				if (pParse.pAinc != null && pParse.nTab == 0)
					pParse.nTab = 1;
				sqlite3VdbeMakeReady(v, pParse);
				pParse.rc = SQLITE_DONE;
				pParse.colNamesSet = 0;
			}
			else
			{
				pParse.rc = SQLITE_ERROR;
			}
			pParse.nTab = 0;
			pParse.nMem = 0;
			pParse.nSet = 0;
			pParse.nVar = 0;
			pParse.cookieMask = 0;
			pParse.cookieGoto = 0;
		}

		/*
		** Run the parser and code generator recursively in order to generate
		** code for the SQL statement given onto the end of the pParse context
		** currently under construction.  When the parser is run recursively
		** this way, the final OP_Halt is not appended and other initialization
		** and finalization steps are omitted because those are handling by the
		** outermost parser.
		**
		** Not everything is nestable.  This facility is designed to permit
		** INSERT, UPDATE, and DELETE operations against SQLITE_MASTER.  Use
		** care if you decide to try to use this routine for some other purposes.
		*/

		private static void sqlite3NestedParse(Parse pParse, string zFormat, params object[] ap)
		{
			string zSql;        //  string zSql;
			string zErrMsg = "";//  char* zErrMsg = 0;
			sqlite3 db = pParse.db;

			//# define SAVE_SZ  (Parse.Length - offsetof(Parse,nVar))
			//  char saveBuf[SAVE_SZ];

			if (pParse.nErr != 0)
				return;
			Debug.Assert(pParse.nested < 10);  /* Nesting should only be of limited depth */
			//  va_list ap;
			lock (lock_va_list)
			{
				va_start(ap, zFormat);
				zSql = sqlite3VMPrintf(db, zFormat, ap);
				va_end(ref ap);
			}
			//if( zSql=="" ){
			//  return;   /* A malloc must have failed */
			//}
			lock (nestingLock)
			{
				pParse.nested++;
				pParse.SaveMembers();     //  memcpy(saveBuf, pParse.nVar, SAVE_SZ);
				pParse.ResetMembers();    //  memset(pParse.nVar, 0, SAVE_SZ);
				sqlite3RunParser(pParse, zSql, ref zErrMsg);
				sqlite3DbFree(db, ref zErrMsg);
				sqlite3DbFree(db, ref zSql);
				pParse.RestoreMembers();  //  memcpy(pParse.nVar, saveBuf, SAVE_SZ);
				pParse.nested--;
			}
		}

		private static Object nestingLock = new Object();

		/*
		** Locate the in-memory structure that describes a particular database
		** table given the name of that table and (optionally) the name of the
		** database containing the table.  Return NULL if not found.
		**
		** If zDatabase is 0, all databases are searched for the table and the
		** first matching table is returned.  (No checking for duplicate table
		** names is done.)  The search order is TEMP first, then MAIN, then any
		** auxiliary databases added using the ATTACH command.
		**
		** See also sqlite3LocateTable().
		*/

		private static Table sqlite3FindTable(sqlite3 db, string zName, string zDatabase)
		{
			Table p = null;
			int i;
			int nName;
			Debug.Assert(zName != null);
			nName = sqlite3Strlen30(zName);
			/* All mutexes are required for schema access.  Make sure we hold them. */
			Debug.Assert(zDatabase != null || sqlite3BtreeHoldsAllMutexes(db));
			for (i = OMIT_TEMPDB; i < db.nDb; i++)
			{
				int j = (i < 2) ? i ^ 1 : i;   /* Search TEMP before MAIN */
				if (zDatabase != null && !zDatabase.Equals(db.aDb[j].zName, StringComparison.OrdinalIgnoreCase))
					continue;
				Debug.Assert(sqlite3SchemaMutexHeld(db, j, null));
				p = sqlite3HashFind(db.aDb[j].pSchema.tblHash, zName, nName, (Table)null);
				if (p != null)
					break;
			}
			return p;
		}

		/*
		** Locate the in-memory structure that describes a particular database
		** table given the name of that table and (optionally) the name of the
		** database containing the table.  Return NULL if not found.  Also leave an
		** error message in pParse.zErrMsg.
		**
		** The difference between this routine and sqlite3FindTable() is that this
		** routine leaves an error message in pParse.zErrMsg where
		** sqlite3FindTable() does not.
		*/

		private static Table sqlite3LocateTable(
		Parse pParse,     /* context in which to report errors */
		int isView,       /* True if looking for a VIEW rather than a TABLE */
		string zName,     /* Name of the table we are looking for */
		string zDbase     /* Name of the database.  Might be NULL */
		)
		{
			Table p;

			/* Read the database schema. If an error occurs, leave an error message
			** and code in pParse and return NULL. */
			if (SQLITE_OK != sqlite3ReadSchema(pParse))
			{
				return null;
			}

			p = sqlite3FindTable(pParse.db, zName, zDbase);
			if (p == null)
			{
				string zMsg = isView != 0 ? "no such view" : "no such table";
				if (zDbase != null)
				{
					sqlite3ErrorMsg(pParse, "%s: %s.%s", zMsg, zDbase, zName);
				}
				else
				{
					sqlite3ErrorMsg(pParse, "%s: %s", zMsg, zName);
				}
				pParse.checkSchema = 1;
			}
			return p;
		}

		/*
		** Locate the in-memory structure that describes
		** a particular index given the name of that index
		** and the name of the database that contains the index.
		** Return NULL if not found.
		**
		** If zDatabase is 0, all databases are searched for the
		** table and the first matching index is returned.  (No checking
		** for duplicate index names is done.)  The search order is
		** TEMP first, then MAIN, then any auxiliary databases added
		** using the ATTACH command.
		*/

		private static Index sqlite3FindIndex(sqlite3 db, string zName, string zDb)
		{
			Index p = null;
			int i;
			int nName = sqlite3Strlen30(zName);
			/* All mutexes are required for schema access.  Make sure we hold them. */
			Debug.Assert(zDb != null || sqlite3BtreeHoldsAllMutexes(db));
			for (i = OMIT_TEMPDB; i < db.nDb; i++)
			{
				int j = (i < 2) ? i ^ 1 : i;  /* Search TEMP before MAIN */
				Schema pSchema = db.aDb[j].pSchema;
				Debug.Assert(pSchema != null);
				if (zDb != null && !zDb.Equals(db.aDb[j].zName, StringComparison.OrdinalIgnoreCase))
					continue;
				Debug.Assert(sqlite3SchemaMutexHeld(db, j, null));
				p = sqlite3HashFind(pSchema.idxHash, zName, nName, (Index)null);
				if (p != null)
					break;
			}
			return p;
		}

		/*
		** Reclaim the memory used by an index
		*/

		private static void freeIndex(sqlite3 db, ref Index p)
		{
#if !SQLITE_OMIT_ANALYZE
			sqlite3DeleteIndexSamples(db, p);
#endif
			sqlite3DbFree(db, ref p.zColAff);
			sqlite3DbFree(db, ref p);
		}

		/*
		** For the index called zIdxName which is found in the database iDb,
		** unlike that index from its Table then remove the index from
		** the index hash table and free all memory structures associated
		** with the index.
		*/

		private static void sqlite3UnlinkAndDeleteIndex(sqlite3 db, int iDb, string zIdxName)
		{
			Index pIndex;
			int len;
			Hash pHash;

			Debug.Assert(sqlite3SchemaMutexHeld(db, iDb, null));
			pHash = db.aDb[iDb].pSchema.idxHash;

			len = sqlite3Strlen30(zIdxName);
			pIndex = sqlite3HashInsert(ref pHash, zIdxName, len, (Index)null);
			if (ALWAYS(pIndex))
			{
				if (pIndex.pTable.pIndex == pIndex)
				{
					pIndex.pTable.pIndex = pIndex.pNext;
				}
				else
				{
					Index p;
					/* Justification of ALWAYS();  The index must be on the list of
					** indices. */
					p = pIndex.pTable.pIndex;
					while (ALWAYS(p != null) && p.pNext != pIndex)
					{
						p = p.pNext;
					}
					if (ALWAYS(p != null && p.pNext == pIndex))
					{
						p.pNext = pIndex.pNext;
					}
				}
				freeIndex(db, ref pIndex);
			}
			db.flags |= SQLITE_InternChanges;
		}

		/*
		** Erase all schema information from the in-memory hash tables of
		** a single database.  This routine is called to reclaim memory
		** before the database closes.  It is also called during a rollback
		** if there were schema changes during the transaction or if a
		** schema-cookie mismatch occurs.
		**
		** If iDb<0 then reset the internal schema tables for all database
		** files.  If iDb>=0 then reset the internal schema for only the
		** single file indicated.
		*/

		private static void sqlite3ResetInternalSchema(sqlite3 db, int iDb)
		{
			int i, j;
			Debug.Assert(iDb < db.nDb);

			if (iDb >= 0)
			{
				/* Case 1:  Reset the single schema identified by iDb */
				Db pDb = db.aDb[iDb];
				Debug.Assert(sqlite3SchemaMutexHeld(db, iDb, null));
				Debug.Assert(pDb.pSchema != null);
				sqlite3SchemaClear(pDb.pSchema);

				/* If any database other than TEMP is reset, then also reset TEMP
				** since TEMP might be holding triggers that reference tables in the
				** other database.
				*/
				if (iDb != 1)
				{
					pDb = db.aDb[1];
					Debug.Assert(pDb.pSchema != null);
					sqlite3SchemaClear(pDb.pSchema);
				}
				return;
			}
			/* Case 2 (from here to the end): Reset all schemas for all attached
			** databases. */
			Debug.Assert(iDb < 0);
			sqlite3BtreeEnterAll(db);
			for (i = 0; i < db.nDb; i++)
			{
				Db pDb = db.aDb[i];
				if (pDb.pSchema != null)
				{
					sqlite3SchemaClear(pDb.pSchema);
				}
			}
			db.flags &= ~SQLITE_InternChanges;
			sqlite3VtabUnlockList(db);
			sqlite3BtreeLeaveAll(db);
			/* If one or more of the auxiliary database files has been closed,
			** then remove them from the auxiliary database list.  We take the
			** opportunity to do this here since we have just deleted all of the
			** schema hash tables and therefore do not have to make any changes
			** to any of those tables.
			*/
			for (i = j = 2; i < db.nDb; i++)
			{
				Db pDb = db.aDb[i];
				if (pDb.pBt == null)
				{
					sqlite3DbFree(db, ref pDb.zName);
					continue;
				}
				if (j < i)
				{
					db.aDb[j] = db.aDb[i];
				}
				j++;
			}
			if (db.nDb != j)
				db.aDb[j] = new Db();//memset(db.aDb[j], 0, (db.nDb-j)*sizeof(db.aDb[j]));
			db.nDb = j;
			if (db.nDb <= 2 && db.aDb != db.aDbStatic)
			{
				Array.Copy(db.aDb, db.aDbStatic, 2);// memcpy(db.aDbStatic, db.aDb, 2*sizeof(db.aDb[0]));
				//sqlite3DbFree( db, ref db.aDb );
				//db.aDb = db.aDbStatic;
			}
		}

		/*
		** This routine is called when a commit occurs.
		*/

		private static void sqlite3CommitInternalChanges(sqlite3 db)
		{
			db.flags &= ~SQLITE_InternChanges;
		}

		/*
		** Delete memory allocated for the column names of a table or view (the
		** Table.aCol[] array).
		*/

		private static void sqliteDeleteColumnNames(sqlite3 db, Table pTable)
		{
			int i;
			Column pCol;
			Debug.Assert(pTable != null);
			for (i = 0; i < pTable.nCol; i++)
			{
				pCol = pTable.aCol[i];
				if (pCol != null)
				{
					sqlite3DbFree(db, ref pCol.zName);
					sqlite3ExprDelete(db, ref pCol.pDflt);
					sqlite3DbFree(db, ref pCol.zDflt);
					sqlite3DbFree(db, ref pCol.zType);
					sqlite3DbFree(db, ref pCol.zColl);
				}
			}
		}

		/*
		** Remove the memory data structures associated with the given
		** Table.  No changes are made to disk by this routine.
		**
		** This routine just deletes the data structure.  It does not unlink
		** the table data structure from the hash table.  But it does destroy
		** memory structures of the indices and foreign keys associated with
		** the table.
		*/

		private static void sqlite3DeleteTable(sqlite3 db, ref Table pTable)
		{
			Index pIndex;
			Index pNext;

			Debug.Assert(null == pTable || pTable.nRef > 0);

			/* Do not delete the table until the reference count reaches zero. */
			if (null == pTable)
				return;
			if ((// ( !db || db->pnBytesFreed == 0 ) &&
			  (--pTable.nRef) > 0))
				return;

			/* Delete all indices associated with this table. */
			for (pIndex = pTable.pIndex; pIndex != null; pIndex = pNext)
			{
				pNext = pIndex.pNext;
				Debug.Assert(pIndex.pSchema == pTable.pSchema);
				//if( null==db || db.pnBytesFreed==0 ){
				string zName = pIndex.zName;
				//
#if !NDEBUG || SQLITE_COVERAGE_TEST
				//  TESTONLY ( Index pOld = ) sqlite3HashInsert(
				//ref pIndex.pSchema.idxHash, zName, sqlite3Strlen30(zName), 0
				//  );
				Index pOld = sqlite3HashInsert(
			  ref pIndex.pSchema.idxHash, zName, sqlite3Strlen30(zName), (Index)null
				);
				Debug.Assert(db == null || sqlite3SchemaMutexHeld(db, 0, pIndex.pSchema));
				Debug.Assert(pOld == pIndex || pOld == null);
#else
    //  TESTONLY ( Index pOld = ) sqlite3HashInsert(
    //ref pIndex.pSchema.idxHash, zName, sqlite3Strlen30(zName), 0
    //  );
      sqlite3HashInsert(
    ref pIndex.pSchema.idxHash, zName, sqlite3Strlen30(zName),(Index)null
      );
#endif
				//}
				freeIndex(db, ref pIndex);
			}

			/* Delete any foreign keys attached to this table. */
			sqlite3FkDelete(db, pTable);

			/* Delete the Table structure itself.
	  */
			sqliteDeleteColumnNames(db, pTable);
			sqlite3DbFree(db, ref pTable.zName);
			sqlite3DbFree(db, ref pTable.zColAff);
			sqlite3SelectDelete(db, ref pTable.pSelect);
#if !SQLITE_OMIT_CHECK
			sqlite3ExprDelete(db, ref pTable.pCheck);
#endif
#if !SQLITE_OMIT_VIRTUALTABLE
			sqlite3VtabClear(db, pTable);
#endif
			pTable = null;//      sqlite3DbFree( db, ref pTable );
		}

		/*
		** Unlink the given table from the hash tables and the delete the
		** table structure with all its indices and foreign keys.
		*/

		private static void sqlite3UnlinkAndDeleteTable(sqlite3 db, int iDb, string zTabName)
		{
			Table p;
			Db pDb;

			Debug.Assert(db != null);
			Debug.Assert(iDb >= 0 && iDb < db.nDb);
			Debug.Assert(zTabName != null);
			Debug.Assert(sqlite3SchemaMutexHeld(db, iDb, null));
			testcase(zTabName.Length == 0);  /* Zero-length table names are allowed */
			pDb = db.aDb[iDb];
			p = sqlite3HashInsert(ref pDb.pSchema.tblHash, zTabName,
			sqlite3Strlen30(zTabName), (Table)null);
			sqlite3DeleteTable(db, ref p);
			db.flags |= SQLITE_InternChanges;
		}

		/*
		** Given a token, return a string that consists of the text of that
		** token.  Space to hold the returned string
		** is obtained from sqliteMalloc() and must be freed by the calling
		** function.
		**
		** Any quotation marks (ex:  "name", 'name', [name], or `name`) that
		** surround the body of the token are removed.
		**
		** Tokens are often just pointers into the original SQL text and so
		** are not \000 terminated and are not persistent.  The returned string
		** is \000 terminated and is persistent.
		*/

		private static string sqlite3NameFromToken(sqlite3 db, Token pName)
		{
			string zName;
			if (pName != null && pName.z != null)
			{
				zName = pName.z.Substring(0, pName.n);//sqlite3DbStrNDup(db, (char)pName.z, pName.n);
				sqlite3Dequote(ref zName);
			}
			else
			{
				return null;
			}
			return zName;
		}

		/*
		** Open the sqlite_master table stored in database number iDb for
		** writing. The table is opened using cursor 0.
		*/

		private static void sqlite3OpenMasterTable(Parse p, int iDb)
		{
			Vdbe v = sqlite3GetVdbe(p);
			sqlite3TableLock(p, iDb, MASTER_ROOT, 1, SCHEMA_TABLE(iDb));
			sqlite3VdbeAddOp3(v, OP_OpenWrite, 0, MASTER_ROOT, iDb);
			sqlite3VdbeChangeP4(v, -1, (int)5, P4_INT32);  /* 5 column table */
			if (p.nTab == 0)
			{
				p.nTab = 1;
			}
		}

		/*
		** Parameter zName points to a nul-terminated buffer containing the name
		** of a database ("main", "temp" or the name of an attached db). This
		** function returns the index of the named database in db->aDb[], or
		** -1 if the named db cannot be found.
		*/

		private static int sqlite3FindDbName(sqlite3 db, string zName)
		{
			int i = -1;    /* Database number */
			if (zName != null)
			{
				Db pDb;
				int n = sqlite3Strlen30(zName);
				for (i = (db.nDb - 1); i >= 0; i--)
				{
					pDb = db.aDb[i];
					if ((OMIT_TEMPDB == 0 || i != 1) && n == sqlite3Strlen30(pDb.zName) &&
					pDb.zName.Equals(zName, StringComparison.OrdinalIgnoreCase))
					{
						break;
					}
				}
			}
			return i;
		}

		/*
		** The token *pName contains the name of a database (either "main" or
		** "temp" or the name of an attached db). This routine returns the
		** index of the named database in db->aDb[], or -1 if the named db
		** does not exist.
		*/

		private static int sqlite3FindDb(sqlite3 db, Token pName)
		{
			int i;                               /* Database number */
			string zName;                         /* Name we are searching for */
			zName = sqlite3NameFromToken(db, pName);
			i = sqlite3FindDbName(db, zName);
			sqlite3DbFree(db, ref zName);
			return i;
		}

		/* The table or view or trigger name is passed to this routine via tokens
		** pName1 and pName2. If the table name was fully qualified, for example:
		**
		** CREATE TABLE xxx.yyy (...);
		**
		** Then pName1 is set to "xxx" and pName2 "yyy". On the other hand if
		** the table name is not fully qualified, i.e.:
		**
		** CREATE TABLE yyy(...);
		**
		** Then pName1 is set to "yyy" and pName2 is "".
		**
		** This routine sets the ppUnqual pointer to point at the token (pName1 or
		** pName2) that stores the unqualified table name.  The index of the
		** database "xxx" is returned.
		*/

		private static int sqlite3TwoPartName(
		Parse pParse,      /* Parsing and code generating context */
		Token pName1,      /* The "xxx" in the name "xxx.yyy" or "xxx" */
		Token pName2,      /* The "yyy" in the name "xxx.yyy" */
		ref Token pUnqual  /* Write the unqualified object name here */
		)
		{
			int iDb;                    /* Database holding the object */
			sqlite3 db = pParse.db;

			if (ALWAYS(pName2 != null) && pName2.n > 0)
			{
				if (db.init.busy != 0)
				{
					sqlite3ErrorMsg(pParse, "corrupt database");
					pParse.nErr++;
					return -1;
				}
				pUnqual = pName2;
				iDb = sqlite3FindDb(db, pName1);
				if (iDb < 0)
				{
					sqlite3ErrorMsg(pParse, "unknown database %T", pName1);
					pParse.nErr++;
					return -1;
				}
			}
			else
			{
				Debug.Assert(db.init.iDb == 0 || db.init.busy != 0);
				iDb = db.init.iDb;
				pUnqual = pName1;
			}
			return iDb;
		}

		/*
		** This routine is used to check if the UTF-8 string zName is a legal
		** unqualified name for a new schema object (vtable, index, view or
		** trigger). All names are legal except those that begin with the string
		** "sqlite_" (in upper, lower or mixed case). This portion of the namespace
		** is reserved for internal use.
		*/

		private static int sqlite3CheckObjectName(Parse pParse, string zName)
		{
			if (0 == pParse.db.init.busy && pParse.nested == 0
			&& (pParse.db.flags & SQLITE_WriteSchema) == 0
			&& zName.StartsWith("sqlite_", System.StringComparison.OrdinalIgnoreCase))
			{
				sqlite3ErrorMsg(pParse, "object name reserved for internal use: %s", zName);
				return SQLITE_ERROR;
			}
			return SQLITE_OK;
		}

		/*
		** Begin constructing a new table representation in memory.  This is
		** the first of several action routines that get called in response
		** to a CREATE TABLE statement.  In particular, this routine is called
		** after seeing tokens "CREATE" and "TABLE" and the table name. The isTemp
		** flag is true if the table should be stored in the auxiliary database
		** file instead of in the main database file.  This is normally the case
		** when the "TEMP" or "TEMPORARY" keyword occurs in between
		** CREATE and TABLE.
		**
		** The new table record is initialized and put in pParse.pNewTable.
		** As more of the CREATE TABLE statement is parsed, additional action
		** routines will be called to add more information to this record.
		** At the end of the CREATE TABLE statement, the sqlite3EndTable() routine
		** is called to complete the construction of the new table record.
		*/

		private static void sqlite3StartTable(
		Parse pParse,   /* Parser context */
		Token pName1,   /* First part of the name of the table or view */
		Token pName2,   /* Second part of the name of the table or view */
		int isTemp,      /* True if this is a TEMP table */
		int isView,      /* True if this is a VIEW */
		int isVirtual,   /* True if this is a VIRTUAL table */
		int noErr        /* Do nothing if table already exists */
		)
		{
			Table pTable;
			string zName = null; /* The name of the new table */
			sqlite3 db = pParse.db;
			Vdbe v;
			int iDb;         /* Database number to create the table in */
			Token pName = new Token();    /* Unqualified name of the table to create */

			/* The table or view name to create is passed to this routine via tokens
			** pName1 and pName2. If the table name was fully qualified, for example:
			**
			** CREATE TABLE xxx.yyy (...);
			**
			** Then pName1 is set to "xxx" and pName2 "yyy". On the other hand if
			** the table name is not fully qualified, i.e.:
			**
			** CREATE TABLE yyy(...);
			**
			** Then pName1 is set to "yyy" and pName2 is "".
			**
			** The call below sets the pName pointer to point at the token (pName1 or
			** pName2) that stores the unqualified table name. The variable iDb is
			** set to the index of the database that the table or view is to be
			** created in.
			*/
			iDb = sqlite3TwoPartName(pParse, pName1, pName2, ref pName);
			if (iDb < 0)
				return;
			if (0 == OMIT_TEMPDB && isTemp != 0 && pName2.n > 0 && iDb != 1)
			{
				/* If creating a temp table, the name may not be qualified. Unless
				** the database name is "temp" anyway.  */

				sqlite3ErrorMsg(pParse, "temporary table name must be unqualified");
				return;
			}
			if (OMIT_TEMPDB == 0 && isTemp != 0)
				iDb = 1;

			pParse.sNameToken = pName;
			zName = sqlite3NameFromToken(db, pName);
			if (zName == null)
				return;
			if (SQLITE_OK != sqlite3CheckObjectName(pParse, zName))
			{
				goto begin_table_error;
			}
			if (db.init.iDb == 1)
				isTemp = 1;
#if !SQLITE_OMIT_AUTHORIZATION
Debug.Assert( (isTemp & 1)==isTemp );
{
int code;
string zDb = db.aDb[iDb].zName;
if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(isTemp), 0, zDb) ){
goto begin_table_error;
}
if( isView ){
if( OMIT_TEMPDB ==0&& isTemp ){
code = SQLITE_CREATE_TEMP_VIEW;
}else{
code = SQLITE_CREATE_VIEW;
}
}else{
if( OMIT_TEMPDB ==0&& isTemp ){
code = SQLITE_CREATE_TEMP_TABLE;
}else{
code = SQLITE_CREATE_TABLE;
}
}
if( null==isVirtual && sqlite3AuthCheck(pParse, code, zName, 0, zDb) ){
goto begin_table_error;
}
}
#endif

			/* Make sure the new table name does not collide with an existing
** index or table name in the same database.  Issue an error message if
** it does. The exception is if the statement being parsed was passed
** to an sqlite3_declare_vtab() call. In that case only the column names
** and types will be used, so there is no need to test for namespace
** collisions.
*/
			if (!IN_DECLARE_VTAB(pParse))
			{
				String zDb = db.aDb[iDb].zName;
				if (SQLITE_OK != sqlite3ReadSchema(pParse))
				{
					goto begin_table_error;
				}
				pTable = sqlite3FindTable(db, zName, zDb);
				if (pTable != null)
				{
					if (noErr == 0)
					{
						sqlite3ErrorMsg(pParse, "table %T already exists", pName);
					}
					else
					{
						Debug.Assert(0 == db.init.busy);
						sqlite3CodeVerifySchema(pParse, iDb);
					}
					goto begin_table_error;
				}
				if (sqlite3FindIndex(db, zName, zDb) != null)
				{
					sqlite3ErrorMsg(pParse, "there is already an index named %s", zName);
					goto begin_table_error;
				}
			}

			pTable = new Table();// sqlite3DbMallocZero(db, Table).Length;
			//if ( pTable == null )
			//{
			//  db.mallocFailed = 1;
			//  pParse.rc = SQLITE_NOMEM;
			//  pParse.nErr++;
			//  goto begin_table_error;
			//}
			pTable.zName = zName;
			pTable.iPKey = -1;
			pTable.pSchema = db.aDb[iDb].pSchema;
			pTable.nRef = 1;
			pTable.nRowEst = 1000000;
			Debug.Assert(pParse.pNewTable == null);
			pParse.pNewTable = pTable;

			/* If this is the magic sqlite_sequence table used by autoincrement,
			** then record a pointer to this table in the main database structure
			** so that INSERT can find the table easily.
			*/
#if !SQLITE_OMIT_AUTOINCREMENT
			if (pParse.nested == 0 && zName == "sqlite_sequence")
			{
				Debug.Assert(sqlite3SchemaMutexHeld(db, iDb, null));
				pTable.pSchema.pSeqTab = pTable;
			}
#endif

			/* Begin generating the code that will insert the table record into
** the SQLITE_MASTER table.  Note in particular that we must go ahead
** and allocate the record number for the table entry now.  Before any
** PRIMARY KEY or UNIQUE keywords are parsed.  Those keywords will cause
** indices to be created and the table record must come before the
** indices.  Hence, the record number for the table must be allocated
** now.
*/
			if (0 == db.init.busy && (v = sqlite3GetVdbe(pParse)) != null)
			{
				int j1;
				int fileFormat;
				int reg1, reg2, reg3;
				sqlite3BeginWriteOperation(pParse, 0, iDb);

				if (isVirtual != 0)
				{
					sqlite3VdbeAddOp0(v, OP_VBegin);
				}

				/* If the file format and encoding in the database have not been set,
				** set them now.
				*/
				reg1 = pParse.regRowid = ++pParse.nMem;
				reg2 = pParse.regRoot = ++pParse.nMem;
				reg3 = ++pParse.nMem;
				sqlite3VdbeAddOp3(v, OP_ReadCookie, iDb, reg3, BTREE_FILE_FORMAT);
				sqlite3VdbeUsesBtree(v, iDb);
				j1 = sqlite3VdbeAddOp1(v, OP_If, reg3);
				fileFormat = (db.flags & SQLITE_LegacyFileFmt) != 0 ?
				1 : SQLITE_MAX_FILE_FORMAT;
				sqlite3VdbeAddOp2(v, OP_Integer, fileFormat, reg3);
				sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, reg3);
				sqlite3VdbeAddOp2(v, OP_Integer, ENC(db), reg3);
				sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_TEXT_ENCODING, reg3);
				sqlite3VdbeJumpHere(v, j1);

				/* This just creates a place-holder record in the sqlite_master table.
				** The record created does not contain anything yet.  It will be replaced
				** by the real entry in code generated at sqlite3EndTable().
				**
				** The rowid for the new entry is left in register pParse->regRowid.
				** The root page number of the new table is left in reg pParse->regRoot.
				** The rowid and root page number values are needed by the code that
				** sqlite3EndTable will generate.
				*/
				if (isView != 0 || isVirtual != 0)
				{
					sqlite3VdbeAddOp2(v, OP_Integer, 0, reg2);
				}
				else
				{
					sqlite3VdbeAddOp2(v, OP_CreateTable, iDb, reg2);
				}
				sqlite3OpenMasterTable(pParse, iDb);
				sqlite3VdbeAddOp2(v, OP_NewRowid, 0, reg1);
				sqlite3VdbeAddOp2(v, OP_Null, 0, reg3);
				sqlite3VdbeAddOp3(v, OP_Insert, 0, reg3, reg1);
				sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
				sqlite3VdbeAddOp0(v, OP_Close);
			}

			/* Normal (non-error) return. */
			return;

		  /* If an error occurs, we jump here */
		begin_table_error:
			sqlite3DbFree(db, ref zName);
			return;
		}

		/*
		** This macro is used to compare two strings in a case-insensitive manner.
		** It is slightly faster than calling sqlite3StrICmp() directly, but
		** produces larger code.
		**
		** WARNING: This macro is not compatible with the strcmp() family. It
		** returns true if the two strings are equal, otherwise false.
		*/
		//#define STRICMP(x, y) (\
		//sqlite3UpperToLower[*(unsigned char )(x)]==   \
		//sqlite3UpperToLower[*(unsigned char )(y)]     \
		//&& sqlite3StrICmp((x)+1,(y)+1)==0 )

		/*
		** Add a new column to the table currently being constructed.
		**
		** The parser calls this routine once for each column declaration
		** in a CREATE TABLE statement.  sqlite3StartTable() gets called
		** first to get things going.  Then this routine is called for each
		** column.
		*/

		private static void sqlite3AddColumn(Parse pParse, Token pName)
		{
			Table p;
			int i;
			string z;
			Column pCol;
			sqlite3 db = pParse.db;
			if ((p = pParse.pNewTable) == null)
				return;
#if SQLITE_MAX_COLUMN || !SQLITE_MAX_COLUMN
			if (p.nCol + 1 > db.aLimit[SQLITE_LIMIT_COLUMN])
			{
				sqlite3ErrorMsg(pParse, "too many columns on %s", p.zName);
				return;
			}
#endif
			z = sqlite3NameFromToken(db, pName);
			if (z == null)
				return;
			for (i = 0; i < p.nCol; i++)
			{
				if (z.Equals(p.aCol[i].zName, StringComparison.OrdinalIgnoreCase))
				{//STRICMP(z, p.aCol[i].zName) ){
					sqlite3ErrorMsg(pParse, "duplicate column name: %s", z);
					sqlite3DbFree(db, ref z);
					return;
				}
			}
			if ((p.nCol & 0x7) == 0)
			{
				//aNew = sqlite3DbRealloc(db,p.aCol,(p.nCol+8)*sizeof(p.aCol[0]));
				//if( aNew==0 ){
				//  sqlite3DbFree(db,ref z);
				//  return;
				//}
				Array.Resize(ref p.aCol, p.nCol + 8);
			}
			p.aCol[p.nCol] = new Column();
			pCol = p.aCol[p.nCol];
			//memset(pCol, 0, sizeof(p.aCol[0]));
			pCol.zName = z;

			/* If there is no type specified, columns have the default affinity
			** 'NONE'. If there is a type specified, then sqlite3AddColumnType() will
			** be called next to set pCol.affinity correctly.
			*/
			pCol.affinity = SQLITE_AFF_NONE;
			p.nCol++;
		}

		/*
		** This routine is called by the parser while in the middle of
		** parsing a CREATE TABLE statement.  A "NOT NULL" constraint has
		** been seen on a column.  This routine sets the notNull flag on
		** the column currently under construction.
		*/

		private static void sqlite3AddNotNull(Parse pParse, int onError)
		{
			Table p;
			p = pParse.pNewTable;
			if (p == null || NEVER(p.nCol < 1))
				return;
			p.aCol[p.nCol - 1].notNull = (u8)onError;
		}

		/*
		** Scan the column type name zType (length nType) and return the
		** associated affinity type.
		**
		** This routine does a case-independent search of zType for the
		** substrings in the following table. If one of the substrings is
		** found, the corresponding affinity is returned. If zType contains
		** more than one of the substrings, entries toward the top of
		** the table take priority. For example, if zType is 'BLOBINT',
		** SQLITE_AFF_INTEGER is returned.
		**
		** Substring     | Affinity
		** --------------------------------
		** 'INT'         | SQLITE_AFF_INTEGER
		** 'CHAR'        | SQLITE_AFF_TEXT
		** 'CLOB'        | SQLITE_AFF_TEXT
		** 'TEXT'        | SQLITE_AFF_TEXT
		** 'BLOB'        | SQLITE_AFF_NONE
		** 'REAL'        | SQLITE_AFF_REAL
		** 'FLOA'        | SQLITE_AFF_REAL
		** 'DOUB'        | SQLITE_AFF_REAL
		**
		** If none of the substrings in the above table are found,
		** SQLITE_AFF_NUMERIC is returned.
		*/

		private static char sqlite3AffinityType(string zIn)
		{
			//u32 h = 0;
			//char aff = SQLITE_AFF_NUMERIC;
			zIn = zIn.ToLower();
			if (zIn.Contains("char") || zIn.Contains("clob") || zIn.Contains("text"))
				return SQLITE_AFF_TEXT;
			if (zIn.Contains("blob"))
				return SQLITE_AFF_NONE;
			if (zIn.Contains("doub") || zIn.Contains("floa") || zIn.Contains("real"))
				return SQLITE_AFF_REAL;
			if (zIn.Contains("int"))
				return SQLITE_AFF_INTEGER;
			return SQLITE_AFF_NUMERIC;
			//      string zEnd = pType.z.Substring(pType.n);

			//      while( zIn!=zEnd ){
			//        h = (h<<8) + sqlite3UpperToLower[*zIn];
			//        zIn++;
			//        if( h==(('c'<<24)+('h'<<16)+('a'<<8)+'r') ){             /* CHAR */
			//          aff = SQLITE_AFF_TEXT;
			//        }else if( h==(('c'<<24)+('l'<<16)+('o'<<8)+'b') ){       /* CLOB */
			//          aff = SQLITE_AFF_TEXT;
			//        }else if( h==(('t'<<24)+('e'<<16)+('x'<<8)+'t') ){       /* TEXT */
			//          aff = SQLITE_AFF_TEXT;
			//        }else if( h==(('b'<<24)+('l'<<16)+('o'<<8)+'b')          /* BLOB */
			//            && (aff==SQLITE_AFF_NUMERIC || aff==SQLITE_AFF_REAL) ){
			//          aff = SQLITE_AFF_NONE;
			//#if !SQLITE_OMIT_FLOATING_POINT
			//        }else if( h==(('r'<<24)+('e'<<16)+('a'<<8)+'l')          /* REAL */
			//            && aff==SQLITE_AFF_NUMERIC ){
			//          aff = SQLITE_AFF_REAL;
			//        }else if( h==(('f'<<24)+('l'<<16)+('o'<<8)+'a')          /* FLOA */
			//            && aff==SQLITE_AFF_NUMERIC ){
			//          aff = SQLITE_AFF_REAL;
			//        }else if( h==(('d'<<24)+('o'<<16)+('u'<<8)+'b')          /* DOUB */
			//            && aff==SQLITE_AFF_NUMERIC ){
			//          aff = SQLITE_AFF_REAL;
			//#endif
			//        }else if( (h&0x00FFFFFF)==(('i'<<16)+('n'<<8)+'t') ){    /* INT */
			//          aff = SQLITE_AFF_INTEGER;
			//          break;
			//        }
			//      }

			//      return aff;
		}

		/*
		** This routine is called by the parser while in the middle of
		** parsing a CREATE TABLE statement.  The pFirst token is the first
		** token in the sequence of tokens that describe the type of the
		** column currently under construction.   pLast is the last token
		** in the sequence.  Use this information to construct a string
		** that contains the typename of the column and store that string
		** in zType.
		*/

		private static void sqlite3AddColumnType(Parse pParse, Token pType)
		{
			Table p;
			Column pCol;

			p = pParse.pNewTable;
			if (p == null || NEVER(p.nCol < 1))
				return;
			pCol = p.aCol[p.nCol - 1];
			Debug.Assert(pCol.zType == null);
			pCol.zType = sqlite3NameFromToken(pParse.db, pType);
			pCol.affinity = sqlite3AffinityType(pCol.zType);
		}

		/*
		** The expression is the default value for the most recently added column
		** of the table currently under construction.
		**
		** Default value expressions must be constant.  Raise an exception if this
		** is not the case.
		**
		** This routine is called by the parser while in the middle of
		** parsing a CREATE TABLE statement.
		*/

		private static void sqlite3AddDefaultValue(Parse pParse, ExprSpan pSpan)
		{
			Table p;
			Column pCol;
			sqlite3 db = pParse.db;
			p = pParse.pNewTable;
			if (p != null)
			{
				pCol = (p.aCol[p.nCol - 1]);
				if (sqlite3ExprIsConstantOrFunction(pSpan.pExpr) == 0)
				{
					sqlite3ErrorMsg(pParse, "default value of column [%s] is not constant",
					pCol.zName);
				}
				else
				{
					/* A copy of pExpr is used instead of the original, as pExpr contains
					** tokens that point to volatile memory. The 'span' of the expression
					** is required by pragma table_info.
					*/
					sqlite3ExprDelete(db, ref pCol.pDflt);
					pCol.pDflt = sqlite3ExprDup(db, pSpan.pExpr, EXPRDUP_REDUCE);
					sqlite3DbFree(db, ref pCol.zDflt);
					pCol.zDflt = pSpan.zStart.Substring(0, pSpan.zStart.Length - pSpan.zEnd.Length);
					//sqlite3DbStrNDup( db, pSpan.zStart,
					//                               (int)( pSpan.zEnd.Length - pSpan.zStart.Length ) );
				}
			}
			sqlite3ExprDelete(db, ref pSpan.pExpr);
		}

		/*
		** Designate the PRIMARY KEY for the table.  pList is a list of names
		** of columns that form the primary key.  If pList is NULL, then the
		** most recently added column of the table is the primary key.
		**
		** A table can have at most one primary key.  If the table already has
		** a primary key (and this is the second primary key) then create an
		** error.
		**
		** If the PRIMARY KEY is on a single column whose datatype is INTEGER,
		** then we will try to use that column as the rowid.  Set the Table.iPKey
		** field of the table under construction to be the index of the
		** INTEGER PRIMARY KEY column.  Table.iPKey is set to -1 if there is
		** no INTEGER PRIMARY KEY.
		**
		** If the key is not an INTEGER PRIMARY KEY, then create a unique
		** index for the key.  No index is created for INTEGER PRIMARY KEYs.
		*/

		// OVERLOADS, so I don't need to rewrite parse.c
		private static void sqlite3AddPrimaryKey(Parse pParse, int null_2, int onError, int autoInc, int sortOrder)
		{
			sqlite3AddPrimaryKey(pParse, null, onError, autoInc, sortOrder);
		}

		private static void sqlite3AddPrimaryKey(
		Parse pParse,    /* Parsing context */
		ExprList pList,  /* List of field names to be indexed */
		int onError,     /* What to do with a uniqueness conflict */
		int autoInc,    /* True if the AUTOINCREMENT keyword is present */
		int sortOrder   /* SQLITE_SO_ASC or SQLITE_SO_DESC */
		)
		{
			Table pTab = pParse.pNewTable;
			string zType = null;
			int iCol = -1, i;
			if (pTab == null || IN_DECLARE_VTAB(pParse))
				goto primary_key_exit;
			if ((pTab.tabFlags & TF_HasPrimaryKey) != 0)
			{
				sqlite3ErrorMsg(pParse,
				"table \"%s\" has more than one primary key", pTab.zName);
				goto primary_key_exit;
			}
			pTab.tabFlags |= TF_HasPrimaryKey;
			if (pList == null)
			{
				iCol = pTab.nCol - 1;
				pTab.aCol[iCol].isPrimKey = 1;
			}
			else
			{
				for (i = 0; i < pList.nExpr; i++)
				{
					for (iCol = 0; iCol < pTab.nCol; iCol++)
					{
						if (pList.a[i].zName.Equals(pTab.aCol[iCol].zName, StringComparison.OrdinalIgnoreCase))
						{
							break;
						}
					}
					if (iCol < pTab.nCol)
					{
						pTab.aCol[iCol].isPrimKey = 1;
					}
				}
				if (pList.nExpr > 1)
					iCol = -1;
			}
			if (iCol >= 0 && iCol < pTab.nCol)
			{
				zType = pTab.aCol[iCol].zType;
			}
			if (zType != null && zType.Equals("INTEGER", StringComparison.OrdinalIgnoreCase)
			&& sortOrder == SQLITE_SO_ASC)
			{
				pTab.iPKey = iCol;
				pTab.keyConf = (byte)onError;
				Debug.Assert(autoInc == 0 || autoInc == 1);
				pTab.tabFlags |= (u8)(autoInc * TF_Autoincrement);
			}
			else if (autoInc != 0)
			{
#if !SQLITE_OMIT_AUTOINCREMENT
				sqlite3ErrorMsg(pParse, "AUTOINCREMENT is only allowed on an " +
				"INTEGER PRIMARY KEY");
#endif
			}
			else
			{
				Index p;
				p = sqlite3CreateIndex(pParse, 0, 0, 0, pList, onError, 0, 0, sortOrder, 0);
				if (p != null)
				{
					p.autoIndex = 2;
				}
				pList = null;
			}

		primary_key_exit:
			sqlite3ExprListDelete(pParse.db, ref pList);
			return;
		}

		/*
		** Add a new CHECK constraint to the table currently under construction.
		*/

		private static void sqlite3AddCheckConstraint(
		Parse pParse,    /* Parsing context */
		Expr pCheckExpr  /* The check expression */
		)
		{
			sqlite3 db = pParse.db;
#if !SQLITE_OMIT_CHECK
			Table pTab = pParse.pNewTable;
			if (pTab != null && !IN_DECLARE_VTAB(pParse))
			{
				pTab.pCheck = sqlite3ExprAnd(db, pTab.pCheck, pCheckExpr);
			}
			else
#endif
			{
				sqlite3ExprDelete(db, ref pCheckExpr);
			}
		}

		/*
		** Set the collation function of the most recently parsed table column
		** to the CollSeq given.
		*/

		private static void sqlite3AddCollateType(Parse pParse, Token pToken)
		{
			Table p;
			int i;
			string zColl;              /* Dequoted name of collation sequence */
			sqlite3 db;

			if ((p = pParse.pNewTable) == null)
				return;
			i = p.nCol - 1;
			db = pParse.db;
			zColl = sqlite3NameFromToken(db, pToken);
			if (zColl == null)
				return;

			if (sqlite3LocateCollSeq(pParse, zColl) != null)
			{
				Index pIdx;
				p.aCol[i].zColl = zColl;

				/* If the column is declared as "<name> PRIMARY KEY COLLATE <type>",
				** then an index may have been created on this column before the
				** collation type was added. Correct this if it is the case.
				*/
				for (pIdx = p.pIndex; pIdx != null; pIdx = pIdx.pNext)
				{
					Debug.Assert(pIdx.nColumn == 1);
					if (pIdx.aiColumn[0] == i)
					{
						pIdx.azColl[0] = p.aCol[i].zColl;
					}
				}
			}
			else
			{
				sqlite3DbFree(db, ref zColl);
			}
		}

		/*
		** This function returns the collation sequence for database native text
		** encoding identified by the string zName, length nName.
		**
		** If the requested collation sequence is not available, or not available
		** in the database native encoding, the collation factory is invoked to
		** request it. If the collation factory does not supply such a sequence,
		** and the sequence is available in another text encoding, then that is
		** returned instead.
		**
		** If no versions of the requested collations sequence are available, or
		** another error occurs, NULL is returned and an error message written into
		** pParse.
		**
		** This routine is a wrapper around sqlite3FindCollSeq().  This routine
		** invokes the collation factory if the named collation cannot be found
		** and generates an error message.
		**
		** See also: sqlite3FindCollSeq(), sqlite3GetCollSeq()
		*/

		private static CollSeq sqlite3LocateCollSeq(Parse pParse, string zName)
		{
			sqlite3 db = pParse.db;
			u8 enc = db.aDb[0].pSchema.enc;// ENC(db);
			u8 initbusy = db.init.busy;
			CollSeq pColl;

			pColl = sqlite3FindCollSeq(db, enc, zName, initbusy);
			if (0 == initbusy && (pColl == null || pColl.xCmp == null))
			{
				pColl = sqlite3GetCollSeq(db, enc, pColl, zName);
				if (pColl == null)
				{
					sqlite3ErrorMsg(pParse, "no such collation sequence: %s", zName);
				}
			}

			return pColl;
		}

		/*
		** Generate code that will increment the schema cookie.
		**
		** The schema cookie is used to determine when the schema for the
		** database changes.  After each schema change, the cookie value
		** changes.  When a process first reads the schema it records the
		** cookie.  Thereafter, whenever it goes to access the database,
		** it checks the cookie to make sure the schema has not changed
		** since it was last read.
		**
		** This plan is not completely bullet-proof.  It is possible for
		** the schema to change multiple times and for the cookie to be
		** set back to prior value.  But schema changes are infrequent
		** and the probability of hitting the same cookie value is only
		** 1 chance in 2^32.  So we're safe enough.
		*/

		private static void sqlite3ChangeCookie(Parse pParse, int iDb)
		{
			int r1 = sqlite3GetTempReg(pParse);
			sqlite3 db = pParse.db;
			Vdbe v = pParse.pVdbe;
			Debug.Assert(sqlite3SchemaMutexHeld(db, iDb, null));
			sqlite3VdbeAddOp2(v, OP_Integer, db.aDb[iDb].pSchema.schema_cookie + 1, r1);
			sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_SCHEMA_VERSION, r1);
			sqlite3ReleaseTempReg(pParse, r1);
		}

		/*
		** Measure the number of characters needed to output the given
		** identifier.  The number returned includes any quotes used
		** but does not include the null terminator.
		**
		** The estimate is conservative.  It might be larger that what is
		** really needed.
		*/

		private static int identLength(string z)
		{
			int n;
			for (n = 0; n < z.Length; n++)
			{
				if (z[n] == (byte)'"')
				{
					n++;
				}
			}
			return n + 2;
		}

		/*
		** The first parameter is a pointer to an output buffer. The second
		** parameter is a pointer to an integer that contains the offset at
		** which to write into the output buffer. This function copies the
		** nul-terminated string pointed to by the third parameter, zSignedIdent,
		** to the specified offset in the buffer and updates *pIdx to refer
		** to the first byte after the last byte written before returning.
		**
		** If the string zSignedIdent consists entirely of alpha-numeric
		** characters, does not begin with a digit and is not an SQL keyword,
		** then it is copied to the output buffer exactly as it is. Otherwise,
		** it is quoted using double-quotes.
		*/

		private static void identPut(StringBuilder z, ref int pIdx, string zSignedIdent)
		{
			string zIdent = zSignedIdent;
			int i;
			int j;
			bool needQuote;
			i = pIdx;
			for (j = 0; j < zIdent.Length; j++)
			{
				if (!sqlite3Isalnum(zIdent[j]) && zIdent[j] != '_')
					break;
			}
			needQuote = sqlite3Isdigit(zIdent[0]) || sqlite3KeywordCode(zIdent, j) != TK_ID;
			if (!needQuote)
			{
				needQuote = (j < zIdent.Length && zIdent[j] != 0);
			}
			if (needQuote)
			{
				if (i == z.Length)
					z.Append('\0');
				z[i++] = '"';
			}
			for (j = 0; j < zIdent.Length; j++)
			{
				if (i == z.Length)
					z.Append('\0');
				z[i++] = zIdent[j];
				if (zIdent[j] == '"')
				{
					if (i == z.Length)
						z.Append('\0');
					z[i++] = '"';
				}
			}
			if (needQuote)
			{
				if (i == z.Length)
					z.Append('\0');
				z[i++] = '"';
			}
			//z[i] = 0;
			pIdx = i;
		}

		/*
		** Generate a CREATE TABLE statement appropriate for the given
		** table.  Memory to hold the text of the statement is obtained
		** from sqliteMalloc() and must be freed by the calling function.
		*/

		private static string createTableStmt(sqlite3 db, Table p)
		{
			int i, k, n;
			StringBuilder zStmt;
			string zSep;
			string zSep2;
			string zEnd;
			Column pCol;
			n = 0;
			for (i = 0; i < p.nCol; i++)
			{//, pCol++){
				pCol = p.aCol[i];
				n += identLength(pCol.zName) + 5;
			}
			n += identLength(p.zName);
			if (n < 50)
			{
				zSep = "";
				zSep2 = ",";
				zEnd = ")";
			}
			else
			{
				zSep = "\n  ";
				zSep2 = ",\n  ";
				zEnd = "\n)";
			}
			n += 35 + 6 * p.nCol;
			zStmt = new StringBuilder(n);
			//zStmt = sqlite3DbMallocRaw(0, n);
			//if( zStmt==0 ){
			//  db.mallocFailed = 1;
			//  return 0;
			//}
			//sqlite3_snprintf(n, zStmt,"CREATE TABLE ");
			zStmt.Append("CREATE TABLE ");
			k = sqlite3Strlen30(zStmt);
			identPut(zStmt, ref k, p.zName);
			zStmt.Append('(');//zStmt[k++] = '(';
			for (i = 0; i < p.nCol; i++)
			{//, pCol++){
				pCol = p.aCol[i];
				string[] azType = new string[]  {
/* SQLITE_AFF_TEXT    */ " TEXT",
/* SQLITE_AFF_NONE    */ "",
/* SQLITE_AFF_NUMERIC */ " NUM",
/* SQLITE_AFF_INTEGER */ " INT",
/* SQLITE_AFF_REAL    */ " REAL"
};
				int len;
				string zType;

				zStmt.Append(zSep);//  sqlite3_snprintf(n-k, zStmt[k], zSep);
				k = sqlite3Strlen30(zStmt);//  k += strlen(zStmt[k]);
				zSep = zSep2;
				identPut(zStmt, ref k, pCol.zName);
				Debug.Assert(pCol.affinity - SQLITE_AFF_TEXT >= 0);
				Debug.Assert(pCol.affinity - SQLITE_AFF_TEXT < ArraySize(azType));
				testcase(pCol.affinity == SQLITE_AFF_TEXT);
				testcase(pCol.affinity == SQLITE_AFF_NONE);
				testcase(pCol.affinity == SQLITE_AFF_NUMERIC);
				testcase(pCol.affinity == SQLITE_AFF_INTEGER);
				testcase(pCol.affinity == SQLITE_AFF_REAL);

				zType = azType[pCol.affinity - SQLITE_AFF_TEXT];
				len = sqlite3Strlen30(zType);
				Debug.Assert(pCol.affinity == SQLITE_AFF_NONE
				|| pCol.affinity == sqlite3AffinityType(zType));
				zStmt.Append(zType);// memcpy( &zStmt[k], zType, len );
				k += len;
				Debug.Assert(k <= n);
			}
			zStmt.Append(zEnd);//sqlite3_snprintf(n-k, zStmt[k], "%s", zEnd);
			return zStmt.ToString();
		}

		/*
		** This routine is called to report the final ")" that terminates
		** a CREATE TABLE statement.
		**
		** The table structure that other action routines have been building
		** is added to the internal hash tables, assuming no errors have
		** occurred.
		**
		** An entry for the table is made in the master table on disk, unless
		** this is a temporary table or db.init.busy==1.  When db.init.busy==1
		** it means we are reading the sqlite_master table because we just
		** connected to the database or because the sqlite_master table has
		** recently changed, so the entry for this table already exists in
		** the sqlite_master table.  We do not want to create it again.
		**
		** If the pSelect argument is not NULL, it means that this routine
		** was called to create a table generated from a
		** "CREATE TABLE ... AS SELECT ..." statement.  The column names of
		** the new table will match the result set of the SELECT.
		*/

		// OVERLOADS, so I don't need to rewrite parse.c
		private static void sqlite3EndTable(Parse pParse, Token pCons, Token pEnd, int null_4)
		{
			sqlite3EndTable(pParse, pCons, pEnd, null);
		}

		private static void sqlite3EndTable(Parse pParse, int null_2, int null_3, Select pSelect)
		{
			sqlite3EndTable(pParse, null, null, pSelect);
		}

		private static void sqlite3EndTable(
		Parse pParse,          /* Parse context */
		Token pCons,           /* The ',' token after the last column defn. */
		Token pEnd,            /* The final ')' token in the CREATE TABLE */
		Select pSelect         /* Select from a "CREATE ... AS SELECT" */
		)
		{
			Table p;
			sqlite3 db = pParse.db;
			int iDb;

			if ((pEnd == null && pSelect == null) /*|| db.mallocFailed != 0 */ )
			{
				return;
			}
			p = pParse.pNewTable;
			if (p == null)
				return;

			Debug.Assert(0 == db.init.busy || pSelect == null);

			iDb = sqlite3SchemaToIndex(db, p.pSchema);

#if !SQLITE_OMIT_CHECK
			/* Resolve names in all CHECK constraint expressions.
*/
			if (p.pCheck != null)
			{
				SrcList sSrc;                   /* Fake SrcList for pParse.pNewTable */
				NameContext sNC;                /* Name context for pParse.pNewTable */

				sNC = new NameContext();// memset(sNC, 0, sizeof(sNC));
				sSrc = new SrcList();// memset(sSrc, 0, sizeof(sSrc));
				sSrc.nSrc = 1;
				sSrc.a = new SrcList_item[1];
				sSrc.a[0] = new SrcList_item();
				sSrc.a[0].zName = p.zName;
				sSrc.a[0].pTab = p;
				sSrc.a[0].iCursor = -1;
				sNC.pParse = pParse;
				sNC.pSrcList = sSrc;
				sNC.isCheck = 1;
				if (sqlite3ResolveExprNames(sNC, ref p.pCheck) != 0)
				{
					return;
				}
			}
#endif // * !SQLITE_OMIT_CHECK) */

			/* If the db.init.busy is 1 it means we are reading the SQL off the
** "sqlite_master" or "sqlite_temp_master" table on the disk.
** So do not write to the disk again.  Extract the root page number
** for the table from the db.init.newTnum field.  (The page number
** should have been put there by the sqliteOpenCb routine.)
*/
			if (db.init.busy != 0)
			{
				p.tnum = db.init.newTnum;
			}

			/* If not initializing, then create a record for the new table
			** in the SQLITE_MASTER table of the database.
			**
			** If this is a TEMPORARY table, write the entry into the auxiliary
			** file instead of into the main database file.
			*/
			if (0 == db.init.busy)
			{
				int n;
				Vdbe v;
				String zType = "";    /* "view" or "table" */
				String zType2 = "";    /* "VIEW" or "TABLE" */
				String zStmt = "";    /* Text of the CREATE TABLE or CREATE VIEW statement */

				v = sqlite3GetVdbe(pParse);
				if (NEVER(v == null))
					return;

				sqlite3VdbeAddOp1(v, OP_Close, 0);

				/*
				** Initialize zType for the new view or table.
				*/
				if (p.pSelect == null)
				{
					/* A regular table */
					zType = "table";
					zType2 = "TABLE";
#if !SQLITE_OMIT_VIEW
				}
				else
				{
					/* A view */
					zType = "view";
					zType2 = "VIEW";
#endif
				}

				/* If this is a CREATE TABLE xx AS SELECT ..., execute the SELECT
				** statement to populate the new table. The root-page number for the
				** new table is in register pParse->regRoot.
				**
				** Once the SELECT has been coded by sqlite3Select(), it is in a
				** suitable state to query for the column names and types to be used
				** by the new table.
				**
				** A shared-cache write-lock is not required to write to the new table,
				** as a schema-lock must have already been obtained to create it. Since
				** a schema-lock excludes all other database users, the write-lock would
				** be redundant.
				*/
				if (pSelect != null)
				{
					SelectDest dest = new SelectDest();
					Table pSelTab;

					Debug.Assert(pParse.nTab == 1);
					sqlite3VdbeAddOp3(v, OP_OpenWrite, 1, pParse.regRoot, iDb);
					sqlite3VdbeChangeP5(v, 1);
					pParse.nTab = 2;
					sqlite3SelectDestInit(dest, SRT_Table, 1);
					sqlite3Select(pParse, pSelect, ref dest);
					sqlite3VdbeAddOp1(v, OP_Close, 1);
					if (pParse.nErr == 0)
					{
						pSelTab = sqlite3ResultSetOfSelect(pParse, pSelect);
						if (pSelTab == null)
							return;
						Debug.Assert(p.aCol == null);
						p.nCol = pSelTab.nCol;
						p.aCol = pSelTab.aCol;
						pSelTab.nCol = 0;
						pSelTab.aCol = null;
						sqlite3DeleteTable(db, ref pSelTab);
					}
				}

				/* Compute the complete text of the CREATE statement */
				if (pSelect != null)
				{
					zStmt = createTableStmt(db, p);
				}
				else
				{
					n = (int)(pParse.sNameToken.z.Length - pEnd.z.Length) + 1;
					zStmt = sqlite3MPrintf(db,
					"CREATE %s %.*s", zType2, n, pParse.sNameToken.z
					);
				}

				/* A slot for the record has already been allocated in the
				** SQLITE_MASTER table.  We just need to update that slot with all
				** the information we've collected.
				*/
				sqlite3NestedParse(pParse,
				"UPDATE %Q.%s " +
				"SET type='%s', name=%Q, tbl_name=%Q, rootpage=#%d, sql=%Q " +
				"WHERE rowid=#%d",
				db.aDb[iDb].zName, SCHEMA_TABLE(iDb),
				zType,
				p.zName,
				p.zName,
				pParse.regRoot,
				zStmt,
				pParse.regRowid
				);
				sqlite3DbFree(db, ref zStmt);
				sqlite3ChangeCookie(pParse, iDb);

#if !SQLITE_OMIT_AUTOINCREMENT
				/* Check to see if we need to create an sqlite_sequence table for
** keeping track of autoincrement keys.
*/
				if ((p.tabFlags & TF_Autoincrement) != 0)
				{
					Db pDb = db.aDb[iDb];
					Debug.Assert(sqlite3SchemaMutexHeld(db, iDb, null));
					if (pDb.pSchema.pSeqTab == null)
					{
						sqlite3NestedParse(pParse,
						"CREATE TABLE %Q.sqlite_sequence(name,seq)",
						pDb.zName
						);
					}
				}
#endif

				/* Reparse everything to update our internal data structures */
				sqlite3VdbeAddParseSchemaOp(v, iDb,
						   sqlite3MPrintf(db, "tbl_name='%q'", p.zName));
			}

			/* Add the table to the in-memory representation of the database.
			*/
			if (db.init.busy != 0)
			{
				Table pOld;
				Schema pSchema = p.pSchema;
				Debug.Assert(sqlite3SchemaMutexHeld(db, iDb, null));
				pOld = sqlite3HashInsert(ref pSchema.tblHash, p.zName,
				sqlite3Strlen30(p.zName), p);
				if (pOld != null)
				{
					Debug.Assert(p == pOld);  /* Malloc must have failed inside HashInsert() */
					//        db.mallocFailed = 1;
					return;
				}
				pParse.pNewTable = null;
				db.nTable++;
				db.flags |= SQLITE_InternChanges;

#if !SQLITE_OMIT_ALTERTABLE
				if (p.pSelect == null)
				{
					string zName = pParse.sNameToken.z;
					int nName;
					Debug.Assert(pSelect == null && pCons != null && pEnd != null);
					if (pCons.z == null)
					{
						pCons = pEnd;
					}
					nName = zName.Length - pCons.z.Length;
					p.addColOffset = 13 + nName; // sqlite3Utf8CharLen(zName, nName);
				}
#endif
			}
		}

#if !SQLITE_OMIT_VIEW
		/*
** The parser calls this routine in order to create a new VIEW
*/

		private static void sqlite3CreateView(
		Parse pParse,     /* The parsing context */
		Token pBegin,     /* The CREATE token that begins the statement */
		Token pName1,     /* The token that holds the name of the view */
		Token pName2,     /* The token that holds the name of the view */
		Select pSelect,   /* A SELECT statement that will become the new view */
		int isTemp,      /* TRUE for a TEMPORARY view */
		int noErr         /* Suppress error messages if VIEW already exists */
		)
		{
			Table p;
			int n;
			string z;//string z;
			Token sEnd;
			DbFixer sFix = new DbFixer();
			Token pName = null;
			int iDb;
			sqlite3 db = pParse.db;

			if (pParse.nVar > 0)
			{
				sqlite3ErrorMsg(pParse, "parameters are not allowed in views");
				sqlite3SelectDelete(db, ref pSelect);
				return;
			}
			sqlite3StartTable(pParse, pName1, pName2, isTemp, 1, 0, noErr);
			p = pParse.pNewTable;
			if (p == null || pParse.nErr != 0)
			{
				sqlite3SelectDelete(db, ref pSelect);
				return;
			}
			sqlite3TwoPartName(pParse, pName1, pName2, ref pName);
			iDb = sqlite3SchemaToIndex(db, p.pSchema);
			if (sqlite3FixInit(sFix, pParse, iDb, "view", pName) != 0
			&& sqlite3FixSelect(sFix, pSelect) != 0
			)
			{
				sqlite3SelectDelete(db, ref pSelect);
				return;
			}

			/* Make a copy of the entire SELECT statement that defines the view.
			** This will force all the Expr.token.z values to be dynamically
			** allocated rather than point to the input string - which means that
			** they will persist after the current sqlite3_exec() call returns.
			*/
			p.pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE);
			sqlite3SelectDelete(db, ref pSelect);
			//if ( db.mallocFailed != 0 )
			//{
			//  return;
			//}
			if (0 == db.init.busy)
			{
				sqlite3ViewGetColumnNames(pParse, p);
			}

			/* Locate the end of the CREATE VIEW statement.  Make sEnd point to
			** the end.
			*/
			sEnd = pParse.sLastToken;
			if (ALWAYS(sEnd.z[0] != 0) && sEnd.z[0] != ';')
			{
				sEnd.z = sEnd.z.Substring(sEnd.n);
			}
			sEnd.n = 0;
			n = (int)(pBegin.z.Length - sEnd.z.Length);//sEnd.z - pBegin.z;
			z = pBegin.z;
			while (ALWAYS(n > 0) && sqlite3Isspace(z[n - 1]))
			{
				n--;
			}
			sEnd.z = z.Substring(n - 1);
			sEnd.n = 1;

			/* Use sqlite3EndTable() to add the view to the SQLITE_MASTER table */
			sqlite3EndTable(pParse, null, sEnd, null);
			return;
		}

#else
    static void sqlite3CreateView(
    Parse pParse,     /* The parsing context */
    Token pBegin,     /* The CREATE token that begins the statement */
    Token pName1,     /* The token that holds the name of the view */
    Token pName2,     /* The token that holds the name of the view */
    Select pSelect,   /* A SELECT statement that will become the new view */
    int isTemp,      /* TRUE for a TEMPORARY view */
    int noErr         /* Suppress error messages if VIEW already exists */
    )
    {
    }
#endif // * SQLITE_OMIT_VIEW */

#if !SQLITE_OMIT_VIEW || !SQLITE_OMIT_VIRTUALTABLE
		/*
** The Table structure pTable is really a VIEW.  Fill in the names of
** the columns of the view in the pTable structure.  Return the number
** of errors.  If an error is seen leave an error message in pParse.zErrMsg.
*/

		private static int sqlite3ViewGetColumnNames(Parse pParse, Table pTable)
		{
			Table pSelTab;    /* A fake table from which we get the result set */
			Select pSel;      /* Copy of the SELECT that implements the view */
			int nErr = 0;     /* Number of errors encountered */
			int n;            /* Temporarily holds the number of cursors assigned */
			sqlite3 db = pParse.db;  /* Database connection for malloc errors */
			dxAuth xAuth;     //)(void*,int,const char*,const char*,const char*,const char);

			Debug.Assert(pTable != null);

#if !SQLITE_OMIT_VIRTUALTABLE
			if (sqlite3VtabCallConnect(pParse, pTable) != 0)
			{
				return SQLITE_ERROR;
			}
#endif
			if (IsVirtual(pTable))
				return 0;

#if !SQLITE_OMIT_VIEW
			/* A positive nCol means the columns names for this view are
** already known.
*/
			if (pTable.nCol > 0)
				return 0;

			/* A negative nCol is a special marker meaning that we are currently
			** trying to compute the column names.  If we enter this routine with
			** a negative nCol, it means two or more views form a loop, like this:
			**
			**     CREATE VIEW one AS SELECT * FROM two;
			**     CREATE VIEW two AS SELECT * FROM one;
			**
			** Actually, the error above is now caught prior to reaching this point.
			** But the following test is still important as it does come up
			** in the following:
			**
			**     CREATE TABLE main.ex1(a);
			**     CREATE TEMP VIEW ex1 AS SELECT a FROM ex1;
			**     SELECT * FROM temp.ex1;
			*/
			if (pTable.nCol < 0)
			{
				sqlite3ErrorMsg(pParse, "view %s is circularly defined", pTable.zName);
				return 1;
			}
			Debug.Assert(pTable.nCol >= 0);

			/* If we get this far, it means we need to compute the table names.
			** Note that the call to sqlite3ResultSetOfSelect() will expand any
			** "*" elements in the results set of the view and will assign cursors
			** to the elements of the FROM clause.  But we do not want these changes
			** to be permanent.  So the computation is done on a copy of the SELECT
			** statement that defines the view.
			*/
			Debug.Assert(pTable.pSelect != null);
			pSel = sqlite3SelectDup(db, pTable.pSelect, 0);
			if (pSel != null)
			{
				u8 enableLookaside = db.lookaside.bEnabled;
				n = pParse.nTab;
				sqlite3SrcListAssignCursors(pParse, pSel.pSrc);
				pTable.nCol = -1;
				db.lookaside.bEnabled = 0;
#if !SQLITE_OMIT_AUTHORIZATION
xAuth = db.xAuth;
db.xAuth = 0;
pSelTab = sqlite3ResultSetOfSelect(pParse, pSel);
db.xAuth = xAuth;
#else
				pSelTab = sqlite3ResultSetOfSelect(pParse, pSel);
#endif
				db.lookaside.bEnabled = enableLookaside;
				pParse.nTab = n;
				if (pSelTab != null)
				{
					Debug.Assert(pTable.aCol == null);
					pTable.nCol = pSelTab.nCol;
					pTable.aCol = pSelTab.aCol;
					pSelTab.nCol = 0;
					pSelTab.aCol = null;
					sqlite3DeleteTable(db, ref pSelTab);
					Debug.Assert(sqlite3SchemaMutexHeld(db, 0, pTable.pSchema));
					pTable.pSchema.flags |= DB_UnresetViews;
				}
				else
				{
					pTable.nCol = 0;
					nErr++;
				}
				sqlite3SelectDelete(db, ref pSel);
			}
			else
			{
				nErr++;
			}
#endif // * SQLITE_OMIT_VIEW */
			return nErr;
		}

#endif // * !SQLITE_OMIT_VIEW) || !SQLITE_OMIT_VIRTUALTABLE) */

#if !SQLITE_OMIT_VIEW
		/*
** Clear the column names from every VIEW in database idx.
*/

		private static void sqliteViewResetAll(sqlite3 db, int idx)
		{
			HashElem i;
			Debug.Assert(sqlite3SchemaMutexHeld(db, idx, null));
			if (!DbHasProperty(db, idx, DB_UnresetViews))
				return;
			//for(i=sqliteHashFirst(&db.aDb[idx].pSchema.tblHash); i;i=sqliteHashNext(i)){
			for (i = db.aDb[idx].pSchema.tblHash.first; i != null; i = i.next)
			{
				Table pTab = (Table)i.data;// sqliteHashData( i );
				if (pTab.pSelect != null)
				{
					sqliteDeleteColumnNames(db, pTab);
					pTab.aCol = null;
					pTab.nCol = 0;
				}
			}
			DbClearProperty(db, idx, DB_UnresetViews);
		}

#else
    //# define sqliteViewResetAll(A,B)
    static void sqliteViewResetAll( sqlite3 A, int B )
    {
    }
#endif // * SQLITE_OMIT_VIEW */

		/*
** This function is called by the VDBE to adjust the internal schema
** used by SQLite when the btree layer moves a table root page. The
** root-page of a table or index in database iDb has changed from iFrom
** to iTo.
**
** Ticket #1728:  The symbol table might still contain information
** on tables and/or indices that are the process of being deleted.
** If you are unlucky, one of those deleted indices or tables might
** have the same rootpage number as the real table or index that is
** being moved.  So we cannot stop searching after the first match
** because the first match might be for one of the deleted indices
** or tables and not the table/index that is actually being moved.
** We must continue looping until all tables and indices with
** rootpage==iFrom have been converted to have a rootpage of iTo
** in order to be certain that we got the right one.
*/
#if !SQLITE_OMIT_AUTOVACUUM

		private static void sqlite3RootPageMoved(sqlite3 db, int iDb, int iFrom, int iTo)
		{
			HashElem pElem;
			Hash pHash;
			Db pDb;

			Debug.Assert(sqlite3SchemaMutexHeld(db, iDb, null));
			pDb = db.aDb[iDb];

			pHash = pDb.pSchema.tblHash;
			for (pElem = pHash.first; pElem != null; pElem = pElem.next)// ( pElem = sqliteHashFirst( pHash ) ; pElem ; pElem = sqliteHashNext( pElem ) )
			{
				Table pTab = (Table)pElem.data;// sqliteHashData( pElem );
				if (pTab.tnum == iFrom)
				{
					pTab.tnum = iTo;
				}
			}
			pHash = pDb.pSchema.idxHash;
			for (pElem = pHash.first; pElem != null; pElem = pElem.next)// ( pElem = sqliteHashFirst( pHash ) ; pElem ; pElem = sqliteHashNext( pElem ) )
			{
				Index pIdx = (Index)pElem.data;// sqliteHashData( pElem );
				if (pIdx.tnum == iFrom)
				{
					pIdx.tnum = iTo;
				}
			}
		}

#endif

		/*
** Write code to erase the table with root-page iTable from database iDb.
** Also write code to modify the sqlite_master table and internal schema
** if a root-page of another table is moved by the btree-layer whilst
** erasing iTable (this can happen with an auto-vacuum database).
*/

		private static void destroyRootPage(Parse pParse, int iTable, int iDb)
		{
			Vdbe v = sqlite3GetVdbe(pParse);
			int r1 = sqlite3GetTempReg(pParse);
			sqlite3VdbeAddOp3(v, OP_Destroy, iTable, r1, iDb);
			sqlite3MayAbort(pParse);
#if !SQLITE_OMIT_AUTOVACUUM
			/* OP_Destroy stores an in integer r1. If this integer
** is non-zero, then it is the root page number of a table moved to
** location iTable. The following code modifies the sqlite_master table to
** reflect this.
**
** The "#NNN" in the SQL is a special constant that means whatever value
** is in register NNN.  See grammar rules associated with the TK_REGISTER
** token for additional information.
*/
			sqlite3NestedParse(pParse,
			"UPDATE %Q.%s SET rootpage=%d WHERE #%d AND rootpage=#%d",
			pParse.db.aDb[iDb].zName, SCHEMA_TABLE(iDb), iTable, r1, r1);
#endif
			sqlite3ReleaseTempReg(pParse, r1);
		}

		/*
		** Write VDBE code to erase table pTab and all associated indices on disk.
		** Code to update the sqlite_master tables and internal schema definitions
		** in case a root-page belonging to another table is moved by the btree layer
		** is also added (this can happen with an auto-vacuum database).
		*/

		private static void destroyTable(Parse pParse, Table pTab)
		{
#if  SQLITE_OMIT_AUTOVACUUM
Index pIdx;
int iDb = sqlite3SchemaToIndex( pParse.db, pTab.pSchema );
destroyRootPage( pParse, pTab.tnum, iDb );
for ( pIdx = pTab.pIndex ; pIdx != null ; pIdx = pIdx.pNext )
{
destroyRootPage( pParse, pIdx.tnum, iDb );
}
#else
			/* If the database may be auto-vacuum capable (if SQLITE_OMIT_AUTOVACUUM
** is not defined), then it is important to call OP_Destroy on the
** table and index root-pages in order, starting with the numerically
** largest root-page number. This guarantees that none of the root-pages
** to be destroyed is relocated by an earlier OP_Destroy. i.e. if the
** following were coded:
**
** OP_Destroy 4 0
** ...
** OP_Destroy 5 0
**
** and root page 5 happened to be the largest root-page number in the
** database, then root page 5 would be moved to page 4 by the
** "OP_Destroy 4 0" opcode. The subsequent "OP_Destroy 5 0" would hit
** a free-list page.
*/
			int iTab = pTab.tnum;
			int iDestroyed = 0;

			while (true)
			{
				Index pIdx;
				int iLargest = 0;

				if (iDestroyed == 0 || iTab < iDestroyed)
				{
					iLargest = iTab;
				}
				for (pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext)
				{
					int iIdx = pIdx.tnum;
					Debug.Assert(pIdx.pSchema == pTab.pSchema);
					if ((iDestroyed == 0 || (iIdx < iDestroyed)) && iIdx > iLargest)
					{
						iLargest = iIdx;
					}
				}
				if (iLargest == 0)
				{
					return;
				}
				else
				{
					int iDb = sqlite3SchemaToIndex(pParse.db, pTab.pSchema);
					destroyRootPage(pParse, iLargest, iDb);
					iDestroyed = iLargest;
				}
			}
#endif
		}

		/*
		** This routine is called to do the work of a DROP TABLE statement.
		** pName is the name of the table to be dropped.
		*/

		private static void sqlite3DropTable(Parse pParse, SrcList pName, int isView, int noErr)
		{
			Table pTab;
			Vdbe v;
			sqlite3 db = pParse.db;
			int iDb;

			//if ( db.mallocFailed != 0 )
			//{
			//  goto exit_drop_table;
			//}
			Debug.Assert(pParse.nErr == 0);
			Debug.Assert(pName.nSrc == 1);
			if (noErr != 0)
				db.suppressErr++;
			pTab = sqlite3LocateTable(pParse, isView,
			pName.a[0].zName, pName.a[0].zDatabase);
			if (noErr != 0)
				db.suppressErr--;

			if (pTab == null)
			{
				if (noErr != 0)
					sqlite3CodeVerifyNamedSchema(pParse, pName.a[0].zDatabase);
				goto exit_drop_table;
			}
			iDb = sqlite3SchemaToIndex(db, pTab.pSchema);
			Debug.Assert(iDb >= 0 && iDb < db.nDb);

			/* If pTab is a virtual table, call ViewGetColumnNames() to ensure
			** it is initialized.
			*/
			if (IsVirtual(pTab) && sqlite3ViewGetColumnNames(pParse, pTab) != 0)
			{
				goto exit_drop_table;
			}
#if !SQLITE_OMIT_AUTHORIZATION
{
int code;
string zTab = SCHEMA_TABLE(iDb);
string zDb = db.aDb[iDb].zName;
string zArg2 = 0;
if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb)){
goto exit_drop_table;
}
if( isView ){
if( OMIT_TEMPDB ==0&& iDb==1 ){
code = SQLITE_DROP_TEMP_VIEW;
}else{
code = SQLITE_DROP_VIEW;
}
}else if( IsVirtual(pTab) ){
code = SQLITE_DROP_VTABLE;
zArg2 = sqlite3GetVTable(db, pTab)->pMod->zName;
}else{
if( OMIT_TEMPDB ==0&& iDb==1 ){
code = SQLITE_DROP_TEMP_TABLE;
}else{
code = SQLITE_DROP_TABLE;
}
}
if( sqlite3AuthCheck(pParse, code, pTab.zName, zArg2, zDb) ){
goto exit_drop_table;
}
if( sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab.zName, 0, zDb) ){
goto exit_drop_table;
}
}
#endif
			if (pTab.zName.StartsWith("sqlite_", System.StringComparison.OrdinalIgnoreCase))
			{
				sqlite3ErrorMsg(pParse, "table %s may not be dropped", pTab.zName);
				goto exit_drop_table;
			}

#if !SQLITE_OMIT_VIEW
			/* Ensure DROP TABLE is not used on a view, and DROP VIEW is not used
** on a table.
*/
			if (isView != 0 && pTab.pSelect == null)
			{
				sqlite3ErrorMsg(pParse, "use DROP TABLE to delete table %s", pTab.zName);
				goto exit_drop_table;
			}
			if (0 == isView && pTab.pSelect != null)
			{
				sqlite3ErrorMsg(pParse, "use DROP VIEW to delete view %s", pTab.zName);
				goto exit_drop_table;
			}
#endif

			/* Generate code to remove the table from the master table
** on disk.
*/
			v = sqlite3GetVdbe(pParse);
			if (v != null)
			{
				Trigger pTrigger;
				Db pDb = db.aDb[iDb];
				sqlite3BeginWriteOperation(pParse, 1, iDb);

#if !SQLITE_OMIT_VIRTUALTABLE
				if (IsVirtual(pTab))
				{
					sqlite3VdbeAddOp0(v, OP_VBegin);
				}
#endif
				sqlite3FkDropTable(pParse, pName, pTab);

				/* Drop all triggers associated with the table being dropped. Code
				** is generated to remove entries from sqlite_master and/or
				** sqlite_temp_master if required.
				*/
				pTrigger = sqlite3TriggerList(pParse, pTab);
				while (pTrigger != null)
				{
					Debug.Assert(pTrigger.pSchema == pTab.pSchema ||
					pTrigger.pSchema == db.aDb[1].pSchema);
					sqlite3DropTriggerPtr(pParse, pTrigger);
					pTrigger = pTrigger.pNext;
				}

#if !SQLITE_OMIT_AUTOINCREMENT
				/* Remove any entries of the sqlite_sequence table associated with
** the table being dropped. This is done before the table is dropped
** at the btree level, in case the sqlite_sequence table needs to
** move as a result of the drop (can happen in auto-vacuum mode).
*/
				if ((pTab.tabFlags & TF_Autoincrement) != 0)
				{
					sqlite3NestedParse(pParse,
					"DELETE FROM %s.sqlite_sequence WHERE name=%Q",
					pDb.zName, pTab.zName
					);
				}
#endif

				/* Drop all SQLITE_MASTER table and index entries that refer to the
** table. The program name loops through the master table and deletes
** every row that refers to a table of the same name as the one being
** dropped. Triggers are handled seperately because a trigger can be
** created in the temp database that refers to a table in another
** database.
*/
				sqlite3NestedParse(pParse,
				"DELETE FROM %Q.%s WHERE tbl_name=%Q and type!='trigger'",
				pDb.zName, SCHEMA_TABLE(iDb), pTab.zName);

				/* Drop any statistics from the sqlite_stat1 table, if it exists */
				if (sqlite3FindTable(db, "sqlite_stat1", db.aDb[iDb].zName) != null)
				{
					sqlite3NestedParse(pParse,
					"DELETE FROM %Q.sqlite_stat1 WHERE tbl=%Q", pDb.zName, pTab.zName
					);
				}

				if (0 == isView && !IsVirtual(pTab))
				{
					destroyTable(pParse, pTab);
				}

				/* Remove the table entry from SQLite's internal schema and modify
				** the schema cookie.
				*/
				if (IsVirtual(pTab))
				{
					sqlite3VdbeAddOp4(v, OP_VDestroy, iDb, 0, 0, pTab.zName, 0);
				}
				sqlite3VdbeAddOp4(v, OP_DropTable, iDb, 0, 0, pTab.zName, 0);
				sqlite3ChangeCookie(pParse, iDb);
			}
			sqliteViewResetAll(db, iDb);

		exit_drop_table:
			sqlite3SrcListDelete(db, ref pName);
		}

		/*
		** This routine is called to create a new foreign key on the table
		** currently under construction.  pFromCol determines which columns
		** in the current table point to the foreign key.  If pFromCol==0 then
		** connect the key to the last column inserted.  pTo is the name of
		** the table referred to.  pToCol is a list of tables in the other
		** pTo table that the foreign key points to.  flags contains all
		** information about the conflict resolution algorithms specified
		** in the ON DELETE, ON UPDATE and ON INSERT clauses.
		**
		** An FKey structure is created and added to the table currently
		** under construction in the pParse.pNewTable field.
		**
		** The foreign key is set for IMMEDIATE processing.  A subsequent call
		** to sqlite3DeferForeignKey() might change this to DEFERRED.
		*/

		// OVERLOADS, so I don't need to rewrite parse.c
		private static void sqlite3CreateForeignKey(Parse pParse, int null_2, Token pTo, ExprList pToCol, int flags)
		{
			sqlite3CreateForeignKey(pParse, null, pTo, pToCol, flags);
		}

		private static void sqlite3CreateForeignKey(
		Parse pParse,       /* Parsing context */
		ExprList pFromCol,  /* Columns in this table that point to other table */
		Token pTo,          /* Name of the other table */
		ExprList pToCol,    /* Columns in the other table */
		int flags           /* Conflict resolution algorithms. */
		)
		{
			sqlite3 db = pParse.db;
#if !SQLITE_OMIT_FOREIGN_KEY
			FKey pFKey = null;
			FKey pNextTo;
			Table p = pParse.pNewTable;
			int nByte;
			int i;
			int nCol;
			//string z;

			Debug.Assert(pTo != null);
			if (p == null || IN_DECLARE_VTAB(pParse))
				goto fk_end;
			if (pFromCol == null)
			{
				int iCol = p.nCol - 1;
				if (NEVER(iCol < 0))
					goto fk_end;
				if (pToCol != null && pToCol.nExpr != 1)
				{
					sqlite3ErrorMsg(pParse, "foreign key on %s" +
					" should reference only one column of table %T",
					p.aCol[iCol].zName, pTo);
					goto fk_end;
				}
				nCol = 1;
			}
			else if (pToCol != null && pToCol.nExpr != pFromCol.nExpr)
			{
				sqlite3ErrorMsg(pParse,
				"number of columns in foreign key does not match the number of " +
				"columns in the referenced table");
				goto fk_end;
			}
			else
			{
				nCol = pFromCol.nExpr;
			}
			//nByte = sizeof(*pFKey) + (nCol-1)*sizeof(pFKey.aCol[0]) + pTo.n + 1;
			//if( pToCol ){
			//  for(i=0; i<pToCol.nExpr; i++){
			//    nByte += sqlite3Strlen30(pToCol->a[i].zName) + 1;
			//  }
			//}
			pFKey = new FKey();//sqlite3DbMallocZero(db, nByte );
			if (pFKey == null)
			{
				goto fk_end;
			}
			pFKey.pFrom = p;
			pFKey.pNextFrom = p.pFKey;
			//z = pFKey.aCol[nCol].zCol;
			pFKey.aCol = new FKey.sColMap[nCol];// z;
			pFKey.aCol[0] = new FKey.sColMap();
			pFKey.zTo = pTo.z.Substring(0, pTo.n);      //memcpy( z, pTo.z, pTo.n );
			//z[pTo.n] = 0;
			sqlite3Dequote(ref pFKey.zTo);
			//z += pTo.n + 1;
			pFKey.nCol = nCol;
			if (pFromCol == null)
			{
				pFKey.aCol[0].iFrom = p.nCol - 1;
			}
			else
			{
				for (i = 0; i < nCol; i++)
				{
					if (pFKey.aCol[i] == null)
						pFKey.aCol[i] = new FKey.sColMap();
					int j;
					for (j = 0; j < p.nCol; j++)
					{
						if (p.aCol[j].zName.Equals(pFromCol.a[i].zName, StringComparison.OrdinalIgnoreCase))
						{
							pFKey.aCol[i].iFrom = j;
							break;
						}
					}
					if (j >= p.nCol)
					{
						sqlite3ErrorMsg(pParse,
						"unknown column \"%s\" in foreign key definition",
						pFromCol.a[i].zName);
						goto fk_end;
					}
				}
			}
			if (pToCol != null)
			{
				for (i = 0; i < nCol; i++)
				{
					////int n = sqlite3Strlen30( pToCol.a[i].zName );
					if (pFKey.aCol[i] == null)
						pFKey.aCol[i] = new FKey.sColMap();
					pFKey.aCol[i].zCol = pToCol.a[i].zName;
					//memcpy( z, pToCol.a[i].zName, n );
					//z[n] = 0;
					//z += n + 1;
				}
			}
			pFKey.isDeferred = 0;
			pFKey.aAction[0] = (u8)(flags & 0xff);            /* ON DELETE action */
			pFKey.aAction[1] = (u8)((flags >> 8) & 0xff);    /* ON UPDATE action */

			Debug.Assert(sqlite3SchemaMutexHeld(db, 0, p.pSchema));
			pNextTo = sqlite3HashInsert(ref p.pSchema.fkeyHash,
				pFKey.zTo, sqlite3Strlen30(pFKey.zTo), pFKey
			);
			//if( pNextTo==pFKey ){
			//  db.mallocFailed = 1;
			//  goto fk_end;
			//}
			if (pNextTo != null)
			{
				Debug.Assert(pNextTo.pPrevTo == null);
				pFKey.pNextTo = pNextTo;
				pNextTo.pPrevTo = pFKey;
			}
			/* Link the foreign key to the table as the last step.
			*/
			p.pFKey = pFKey;
			pFKey = null;

		fk_end:
			sqlite3DbFree(db, ref pFKey);
#endif // * !SQLITE_OMIT_FOREIGN_KEY) */
			sqlite3ExprListDelete(db, ref pFromCol);
			sqlite3ExprListDelete(db, ref pToCol);
		}

		/*
		** This routine is called when an INITIALLY IMMEDIATE or INITIALLY DEFERRED
		** clause is seen as part of a foreign key definition.  The isDeferred
		** parameter is 1 for INITIALLY DEFERRED and 0 for INITIALLY IMMEDIATE.
		** The behavior of the most recently created foreign key is adjusted
		** accordingly.
		*/

		private static void sqlite3DeferForeignKey(Parse pParse, int isDeferred)
		{
#if !SQLITE_OMIT_FOREIGN_KEY
			Table pTab;
			FKey pFKey;
			if ((pTab = pParse.pNewTable) == null || (pFKey = pTab.pFKey) == null)
				return;
			Debug.Assert(isDeferred == 0 || isDeferred == 1); /* EV: R-30323-21917 */
			pFKey.isDeferred = (u8)isDeferred;
#endif
		}

		/*
		** Generate code that will erase and refill index pIdx.  This is
		** used to initialize a newly created index or to recompute the
		** content of an index in response to a REINDEX command.
		**
		** if memRootPage is not negative, it means that the index is newly
		** created.  The register specified by memRootPage contains the
		** root page number of the index.  If memRootPage is negative, then
		** the index already exists and must be cleared before being refilled and
		** the root page number of the index is taken from pIndex.tnum.
		*/

		private static void sqlite3RefillIndex(Parse pParse, Index pIndex, int memRootPage)
		{
			Table pTab = pIndex.pTable;   /* The table that is indexed */
			int iTab = pParse.nTab++;     /* Btree cursor used for pTab */
			int iIdx = pParse.nTab++;     /* Btree cursor used for pIndex */
			int addr1;                    /* Address of top of loop */
			int tnum;                     /* Root page of index */
			Vdbe v;                       /* Generate code into this virtual machine */
			KeyInfo pKey;                 /* KeyInfo for index */
			int regIdxKey;                /* Registers containing the index key */
			int regRecord;                /* Register holding assemblied index record */
			sqlite3 db = pParse.db;       /* The database connection */
			int iDb = sqlite3SchemaToIndex(db, pIndex.pSchema);

#if !SQLITE_OMIT_AUTHORIZATION
if( sqlite3AuthCheck(pParse, SQLITE_REINDEX, pIndex.zName, 0,
db.aDb[iDb].zName ) ){
return;
}
#endif

			/* Require a write-lock on the table to perform this operation */
			sqlite3TableLock(pParse, iDb, pTab.tnum, 1, pTab.zName);
			v = sqlite3GetVdbe(pParse);
			if (v == null)
				return;
			if (memRootPage >= 0)
			{
				tnum = memRootPage;
			}
			else
			{
				tnum = pIndex.tnum;
				sqlite3VdbeAddOp2(v, OP_Clear, tnum, iDb);
			}
			pKey = sqlite3IndexKeyinfo(pParse, pIndex);
			sqlite3VdbeAddOp4(v, OP_OpenWrite, iIdx, tnum, iDb,
			pKey, P4_KEYINFO_HANDOFF);
			if (memRootPage >= 0)
			{
				sqlite3VdbeChangeP5(v, 1);
			}
			sqlite3OpenTable(pParse, iTab, iDb, pTab, OP_OpenRead);
			addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iTab, 0);
			regRecord = sqlite3GetTempReg(pParse);
			regIdxKey = sqlite3GenerateIndexKey(pParse, pIndex, iTab, regRecord, true);
			if (pIndex.onError != OE_None)
			{
				int regRowid = regIdxKey + pIndex.nColumn;
				int j2 = sqlite3VdbeCurrentAddr(v) + 2;
				int pRegKey = regIdxKey;// SQLITE_INT_TO_PTR( regIdxKey );

				/* The registers accessed by the OP_IsUnique opcode were allocated
				** using sqlite3GetTempRange() inside of the sqlite3GenerateIndexKey()
				** call above. Just before that function was freed they were released
				** (made available to the compiler for reuse) using
				** sqlite3ReleaseTempRange(). So in some ways having the OP_IsUnique
				** opcode use the values stored within seems dangerous. However, since
				** we can be sure that no other temp registers have been allocated
				** since sqlite3ReleaseTempRange() was called, it is safe to do so.
				*/
				sqlite3VdbeAddOp4(v, OP_IsUnique, iIdx, j2, regRowid, pRegKey, P4_INT32);
				sqlite3HaltConstraint(
					pParse, OE_Abort, "indexed columns are not unique", P4_STATIC);
			}
			sqlite3VdbeAddOp2(v, OP_IdxInsert, iIdx, regRecord);
			sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
			sqlite3ReleaseTempReg(pParse, regRecord);
			sqlite3VdbeAddOp2(v, OP_Next, iTab, addr1 + 1);
			sqlite3VdbeJumpHere(v, addr1);
			sqlite3VdbeAddOp1(v, OP_Close, iTab);
			sqlite3VdbeAddOp1(v, OP_Close, iIdx);
		}

		/*
		** Create a new index for an SQL table.  pName1.pName2 is the name of the index
		** and pTblList is the name of the table that is to be indexed.  Both will
		** be NULL for a primary key or an index that is created to satisfy a
		** UNIQUE constraint.  If pTable and pIndex are NULL, use pParse.pNewTable
		** as the table to be indexed.  pParse.pNewTable is a table that is
		** currently being constructed by a CREATE TABLE statement.
		**
		** pList is a list of columns to be indexed.  pList will be NULL if this
		** is a primary key or unique-constraint on the most recent column added
		** to the table currently under construction.
		**
		** If the index is created successfully, return a pointer to the new Index
		** structure. This is used by sqlite3AddPrimaryKey() to mark the index
		** as the tables primary key (Index.autoIndex==2).
		*/

		// OVERLOADS, so I don't need to rewrite parse.c
		private static Index sqlite3CreateIndex(Parse pParse, int null_2, int null_3, int null_4, int null_5, int onError, int null_7, int null_8, int sortOrder, int ifNotExist)
		{
			return sqlite3CreateIndex(pParse, null, null, null, null, onError, null, null, sortOrder, ifNotExist);
		}

		private static Index sqlite3CreateIndex(Parse pParse, int null_2, int null_3, int null_4, ExprList pList, int onError, int null_7, int null_8, int sortOrder, int ifNotExist)
		{
			return sqlite3CreateIndex(pParse, null, null, null, pList, onError, null, null, sortOrder, ifNotExist);
		}

		private static Index sqlite3CreateIndex(
		Parse pParse,     /* All information about this Parse */
		Token pName1,     /* First part of index name. May be NULL */
		Token pName2,     /* Second part of index name. May be NULL */
		SrcList pTblName, /* Table to index. Use pParse.pNewTable if 0 */
		ExprList pList,   /* A list of columns to be indexed */
		int onError,      /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
		Token pStart,     /* The CREATE token that begins this statement */
		Token pEnd,       /* The ")" that closes the CREATE INDEX statement */
		int sortOrder,    /* Sort order of primary key when pList==NULL */
		int ifNotExist    /* Omit error if index already exists */
		)
		{
			Index pRet = null;            /* Pointer to return */
			Table pTab = null;            /* Table to be indexed */
			Index pIndex = null;          /* The index to be created */
			string zName = null;          /* Name of the index */
			int nName;                    /* Number of characters in zName */
			int i, j;
			Token nullId = new Token();   /* Fake token for an empty ID list */
			DbFixer sFix = new DbFixer(); /* For assigning database names to pTable */
			int sortOrderMask;            /* 1 to honor DESC in index.  0 to ignore. */
			sqlite3 db = pParse.db;
			Db pDb;                       /* The specific table containing the indexed database */
			int iDb;                      /* Index of the database that is being written */
			Token pName = null;           /* Unqualified name of the index to create */
			ExprList_item pListItem;      /* For looping over pList */
			int nCol;
			int nExtra = 0;
			StringBuilder zExtra = new StringBuilder();

			Debug.Assert(pStart == null || pEnd != null); /* pEnd must be non-NULL if pStart is */
			Debug.Assert(pParse.nErr == 0);      /* Never called with prior errors */
			if ( /* db.mallocFailed != 0  || */
			 IN_DECLARE_VTAB(pParse))
			{
				goto exit_create_index;
			}
			if (SQLITE_OK != sqlite3ReadSchema(pParse))
			{
				goto exit_create_index;
			}

			/*
			** Find the table that is to be indexed.  Return early if not found.
			*/
			if (pTblName != null)
			{
				/* Use the two-part index name to determine the database
				** to search for the table. 'Fix' the table name to this db
				** before looking up the table.
				*/
				Debug.Assert(pName1 != null && pName2 != null);
				iDb = sqlite3TwoPartName(pParse, pName1, pName2, ref pName);
				if (iDb < 0)
					goto exit_create_index;

#if !SQLITE_OMIT_TEMPDB
				/* If the index name was unqualified, check if the the table
** is a temp table. If so, set the database to 1. Do not do this
** if initialising a database schema.
*/
				if (0 == db.init.busy)
				{
					pTab = sqlite3SrcListLookup(pParse, pTblName);
					if (pName2.n == 0 && pTab != null && pTab.pSchema == db.aDb[1].pSchema)
					{
						iDb = 1;
					}
				}
#endif

				if (sqlite3FixInit(sFix, pParse, iDb, "index", pName) != 0 &&
				sqlite3FixSrcList(sFix, pTblName) != 0
				)
				{
					/* Because the parser constructs pTblName from a single identifier,
					** sqlite3FixSrcList can never fail. */
					Debugger.Break();
				}
				pTab = sqlite3LocateTable(pParse, 0, pTblName.a[0].zName,
				pTblName.a[0].zDatabase);
				if (pTab == null /*|| db.mallocFailed != 0 */ )
					goto exit_create_index;
				Debug.Assert(db.aDb[iDb].pSchema == pTab.pSchema);
			}
			else
			{
				Debug.Assert(pName == null);
				pTab = pParse.pNewTable;
				if (pTab == null)
					goto exit_create_index;
				iDb = sqlite3SchemaToIndex(db, pTab.pSchema);
			}
			pDb = db.aDb[iDb];

			Debug.Assert(pTab != null);
			Debug.Assert(pParse.nErr == 0);

			if (pTab.zName.StartsWith("sqlite_", System.StringComparison.OrdinalIgnoreCase)
			  && !pTab.zName.StartsWith("sqlite_altertab_", System.StringComparison.OrdinalIgnoreCase))
			{
				sqlite3ErrorMsg(pParse, "table %s may not be indexed", pTab.zName);
				goto exit_create_index;
			}
#if !SQLITE_OMIT_VIEW
			if (pTab.pSelect != null)
			{
				sqlite3ErrorMsg(pParse, "views may not be indexed");
				goto exit_create_index;
			}
#endif
			if (IsVirtual(pTab))
			{
				sqlite3ErrorMsg(pParse, "virtual tables may not be indexed");
				goto exit_create_index;
			}

			/*
			** Find the name of the index.  Make sure there is not already another
			** index or table with the same name.
			**
			** Exception:  If we are reading the names of permanent indices from the
			** sqlite_master table (because some other process changed the schema) and
			** one of the index names collides with the name of a temporary table or
			** index, then we will continue to process this index.
			**
			** If pName==0 it means that we are
			** dealing with a primary key or UNIQUE constraint.  We have to invent our
			** own name.
			*/
			if (pName != null)
			{
				zName = sqlite3NameFromToken(db, pName);
				if (zName == null)
					goto exit_create_index;
				if (SQLITE_OK != sqlite3CheckObjectName(pParse, zName))
				{
					goto exit_create_index;
				}
				if (0 == db.init.busy)
				{
					if (sqlite3FindTable(db, zName, null) != null)
					{
						sqlite3ErrorMsg(pParse, "there is already a table named %s", zName);
						goto exit_create_index;
					}
				}
				if (sqlite3FindIndex(db, zName, pDb.zName) != null)
				{
					if (ifNotExist == 0)
					{
						sqlite3ErrorMsg(pParse, "index %s already exists", zName);
					}
					else
					{
						Debug.Assert(0 == db.init.busy);
						sqlite3CodeVerifySchema(pParse, iDb);
					}
					goto exit_create_index;
				}
			}
			else
			{
				int n = 0;
				Index pLoop;
				for (pLoop = pTab.pIndex, n = 1; pLoop != null; pLoop = pLoop.pNext, n++)
				{
				}
				zName = sqlite3MPrintf(db, "sqlite_autoindex_%s_%d", pTab.zName, n);
				if (zName == null)
				{
					goto exit_create_index;
				}
			}

			/* Check for authorization to create an index.
			*/
#if !SQLITE_OMIT_AUTHORIZATION
{
string zDb = pDb.zName;
if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(iDb), 0, zDb) ){
goto exit_create_index;
}
i = SQLITE_CREATE_INDEX;
if( OMIT_TEMPDB ==0&& iDb==1 ) i = SQLITE_CREATE_TEMP_INDEX;
if( sqlite3AuthCheck(pParse, i, zName, pTab.zName, zDb) ){
goto exit_create_index;
}
}
#endif

			/* If pList==0, it means this routine was called to make a primary
** key out of the last column added to the table under construction.
** So create a fake list to simulate this.
*/
			if (pList == null)
			{
				nullId.z = pTab.aCol[pTab.nCol - 1].zName;
				nullId.n = sqlite3Strlen30(nullId.z);
				pList = sqlite3ExprListAppend(pParse, null, null);
				if (pList == null)
					goto exit_create_index;
				sqlite3ExprListSetName(pParse, pList, nullId, 0);
				pList.a[0].sortOrder = (u8)sortOrder;
			}

			/* Figure out how many bytes of space are required to store explicitly
			** specified collation sequence names.
			*/
			for (i = 0; i < pList.nExpr; i++)
			{
				Expr pExpr = pList.a[i].pExpr;
				if (pExpr != null)
				{
					CollSeq pColl = pExpr.pColl;
					/* Either pColl!=0 or there was an OOM failure.  But if an OOM
					** failure we have quit before reaching this point. */
					if (ALWAYS(pColl != null))
					{
						nExtra += (1 + sqlite3Strlen30(pColl.zName));
					}
				}
			}

			/*
			** Allocate the index structure.
			*/
			nName = sqlite3Strlen30(zName);
			nCol = pList.nExpr;
			pIndex = new Index();
			// sqlite3DbMallocZero( db,
			//    Index.Length +              /* Index structure  */
			//    sizeof( int ) * nCol +           /* Index.aiColumn   */
			//    sizeof( int ) * ( nCol + 1 ) +       /* Index.aiRowEst   */
			//    sizeof( char* ) * nCol +        /* Index.azColl     */
			//    u8.Length * nCol +            /* Index.aSortOrder */
			//    nName + 1 +                  /* Index.zName      */
			//    nExtra                       /* Collation sequence names */
			//);
			//if ( db.mallocFailed != 0 )
			//{
			//  goto exit_create_index;
			//}
			pIndex.azColl = new string[nCol + 1];//(char*)(pIndex[1]);
			pIndex.aiColumn = new int[nCol + 1];//(int )(pIndex->azColl[nCol]);
			pIndex.aiRowEst = new int[nCol + 1];//(unsigned )(pIndex->aiColumn[nCol]);
			pIndex.aSortOrder = new byte[nCol + 1];//(u8 )(pIndex->aiRowEst[nCol+1]);
			//pIndex.zName = null;// (char)( &pIndex->aSortOrder[nCol] );
			zExtra = new StringBuilder(nName + 1);// (char)( &pIndex.zName[nName + 1] );
			if (zName.Length == nName)
				pIndex.zName = zName;
			else
			{
				pIndex.zName = zName.Substring(0, nName);
			}// memcpy( pIndex.zName, zName, nName + 1 );
			pIndex.pTable = pTab;
			pIndex.nColumn = pList.nExpr;
			pIndex.onError = (u8)onError;
			pIndex.autoIndex = (u8)(pName == null ? 1 : 0);
			pIndex.pSchema = db.aDb[iDb].pSchema;
			Debug.Assert(sqlite3SchemaMutexHeld(db, iDb, null));

			/* Check to see if we should honor DESC requests on index columns
			*/
			if (pDb.pSchema.file_format >= 4)
			{
				sortOrderMask = 1;   /* Honor DESC */
			}
			else
			{
				sortOrderMask = 0;    /* Ignore DESC */
			}

			/* Scan the names of the columns of the table to be indexed and
			** load the column indices into the Index structure.  Report an error
			** if any column is not found.
			**
			** TODO:  Add a test to make sure that the same column is not named
			** more than once within the same index.  Only the first instance of
			** the column will ever be used by the optimizer.  Note that using the
			** same column more than once cannot be an error because that would
			** break backwards compatibility - it needs to be a warning.
			*/
			for (i = 0; i < pList.nExpr; i++)
			{//, pListItem++){
				pListItem = pList.a[i];
				string zColName = pListItem.zName;
				Column pTabCol;
				byte requestedSortOrder;
				string zColl;                   /* Collation sequence name */

				for (j = 0; j < pTab.nCol; j++)
				{//, pTabCol++){
					pTabCol = pTab.aCol[j];
					if (zColName.Equals(pTabCol.zName, StringComparison.OrdinalIgnoreCase))
						break;
				}
				if (j >= pTab.nCol)
				{
					sqlite3ErrorMsg(pParse, "table %s has no column named %s",
					pTab.zName, zColName);
					pParse.checkSchema = 1;
					goto exit_create_index;
				}
				pIndex.aiColumn[i] = j;
				/* Justification of the ALWAYS(pListItem->pExpr->pColl):  Because of
				** the way the "idxlist" non-terminal is constructed by the parser,
				** if pListItem->pExpr is not null then either pListItem->pExpr->pColl
				** must exist or else there must have been an OOM error.  But if there
				** was an OOM error, we would never reach this point. */
				if (pListItem.pExpr != null && ALWAYS(pListItem.pExpr.pColl))
				{
					int nColl;
					zColl = pListItem.pExpr.pColl.zName;
					nColl = sqlite3Strlen30(zColl);
					Debug.Assert(nExtra >= nColl);
					zExtra = new StringBuilder(zColl.Substring(0, nColl));// memcpy( zExtra, zColl, nColl );
					zColl = zExtra.ToString();
					//zExtra += nColl;
					nExtra -= nColl;
				}
				else
				{
					zColl = pTab.aCol[j].zColl;
					if (zColl == null)
					{
						zColl = db.pDfltColl.zName;
					}
				}
				if (0 == db.init.busy && sqlite3LocateCollSeq(pParse, zColl) == null)
				{
					goto exit_create_index;
				}
				pIndex.azColl[i] = zColl;
				requestedSortOrder = (u8)((pListItem.sortOrder & sortOrderMask) != 0 ? 1 : 0);
				pIndex.aSortOrder[i] = (u8)requestedSortOrder;
			}
			sqlite3DefaultRowEst(pIndex);

			if (pTab == pParse.pNewTable)
			{
				/* This routine has been called to create an automatic index as a
				** result of a PRIMARY KEY or UNIQUE clause on a column definition, or
				** a PRIMARY KEY or UNIQUE clause following the column definitions.
				** i.e. one of:
				**
				** CREATE TABLE t(x PRIMARY KEY, y);
				** CREATE TABLE t(x, y, UNIQUE(x, y));
				**
				** Either way, check to see if the table already has such an index. If
				** so, don't bother creating this one. This only applies to
				** automatically created indices. Users can do as they wish with
				** explicit indices.
				**
				** Two UNIQUE or PRIMARY KEY constraints are considered equivalent
				** (and thus suppressing the second one) even if they have different
				** sort orders.
				**
				** If there are different collating sequences or if the columns of
				** the constraint occur in different orders, then the constraints are
				** considered distinct and both result in separate indices.
				*/
				Index pIdx;
				for (pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext)
				{
					int k;
					Debug.Assert(pIdx.onError != OE_None);
					Debug.Assert(pIdx.autoIndex != 0);
					Debug.Assert(pIndex.onError != OE_None);

					if (pIdx.nColumn != pIndex.nColumn)
						continue;
					for (k = 0; k < pIdx.nColumn; k++)
					{
						string z1;
						string z2;
						if (pIdx.aiColumn[k] != pIndex.aiColumn[k])
							break;
						z1 = pIdx.azColl[k];
						z2 = pIndex.azColl[k];
						if (z1 != z2 && !z1.Equals(z2, StringComparison.OrdinalIgnoreCase))
							break;
					}
					if (k == pIdx.nColumn)
					{
						if (pIdx.onError != pIndex.onError)
						{
							/* This constraint creates the same index as a previous
							** constraint specified somewhere in the CREATE TABLE statement.
							** However the ON CONFLICT clauses are different. If both this
							** constraint and the previous equivalent constraint have explicit
							** ON CONFLICT clauses this is an error. Otherwise, use the
							** explicitly specified behavior for the index.
							*/
							if (!(pIdx.onError == OE_Default || pIndex.onError == OE_Default))
							{
								sqlite3ErrorMsg(pParse,
								"conflicting ON CONFLICT clauses specified", 0);
							}
							if (pIdx.onError == OE_Default)
							{
								pIdx.onError = pIndex.onError;
							}
						}
						goto exit_create_index;
					}
				}
			}

			/* Link the new Index structure to its table and to the other
			** in-memory database structures.
			*/
			if (db.init.busy != 0)
			{
				Index p;
				Debug.Assert(sqlite3SchemaMutexHeld(db, 0, pIndex.pSchema));
				p = sqlite3HashInsert(ref pIndex.pSchema.idxHash,
				pIndex.zName, sqlite3Strlen30(pIndex.zName),
				pIndex);
				if (p != null)
				{
					Debug.Assert(p == pIndex);  /* Malloc must have failed */
					//        db.mallocFailed = 1;
					goto exit_create_index;
				}
				db.flags |= SQLITE_InternChanges;
				if (pTblName != null)
				{
					pIndex.tnum = db.init.newTnum;
				}
			}

				/* If the db.init.busy is 0 then create the index on disk.  This
				** involves writing the index into the master table and filling in the
				** index with the current table contents.
				**
				** The db.init.busy is 0 when the user first enters a CREATE INDEX
				** command.  db.init.busy is 1 when a database is opened and
				** CREATE INDEX statements are read out of the master table.  In
				** the latter case the index already exists on disk, which is why
				** we don't want to recreate it.
				**
				** If pTblName==0 it means this index is generated as a primary key
				** or UNIQUE constraint of a CREATE TABLE statement.  Since the table
				** has just been created, it contains no data and the index initialization
				** step can be skipped.
				*/
			else //if ( 0 == db.init.busy )
			{
				Vdbe v;
				string zStmt;
				int iMem = ++pParse.nMem;

				v = sqlite3GetVdbe(pParse);
				if (v == null)
					goto exit_create_index;

				/* Create the rootpage for the index
				*/
				sqlite3BeginWriteOperation(pParse, 1, iDb);
				sqlite3VdbeAddOp2(v, OP_CreateIndex, iDb, iMem);

				/* Gather the complete text of the CREATE INDEX statement into
				** the zStmt variable
				*/
				if (pStart != null)
				{
					Debug.Assert(pEnd != null);
					/* A named index with an explicit CREATE INDEX statement */
					zStmt = sqlite3MPrintf(db, "CREATE%s INDEX %.*s",
					onError == OE_None ? "" : " UNIQUE",
					 (int)(pName.z.Length - pEnd.z.Length) + 1,
					pName.z);
				}
				else
				{
					/* An automatic index created by a PRIMARY KEY or UNIQUE constraint */
					/* zStmt = sqlite3MPrintf(""); */
					zStmt = null;
				}

				/* Add an entry in sqlite_master for this index
				*/
				sqlite3NestedParse(pParse,
				"INSERT INTO %Q.%s VALUES('index',%Q,%Q,#%d,%Q);",
				db.aDb[iDb].zName, SCHEMA_TABLE(iDb),
				pIndex.zName,
				pTab.zName,
				iMem,
				zStmt
				);
				sqlite3DbFree(db, ref zStmt);

				/* Fill the index with data and reparse the schema. Code an OP_Expire
				** to invalidate all pre-compiled statements.
				*/
				if (pTblName != null)
				{
					sqlite3RefillIndex(pParse, pIndex, iMem);
					sqlite3ChangeCookie(pParse, iDb);
					sqlite3VdbeAddParseSchemaOp(v, iDb,
					  sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex.zName));
					sqlite3VdbeAddOp1(v, OP_Expire, 0);
				}
			}

			/* When adding an index to the list of indices for a table, make
			** sure all indices labeled OE_Replace come after all those labeled
			** OE_Ignore.  This is necessary for the correct constraint check
			** processing (in sqlite3GenerateConstraintChecks()) as part of
			** UPDATE and INSERT statements.
			*/
			if (db.init.busy != 0 || pTblName == null)
			{
				if (onError != OE_Replace || pTab.pIndex == null
				|| pTab.pIndex.onError == OE_Replace)
				{
					pIndex.pNext = pTab.pIndex;
					pTab.pIndex = pIndex;
				}
				else
				{
					Index pOther = pTab.pIndex;
					while (pOther.pNext != null && pOther.pNext.onError != OE_Replace)
					{
						pOther = pOther.pNext;
					}
					pIndex.pNext = pOther.pNext;
					pOther.pNext = pIndex;
				}
				pRet = pIndex;
				pIndex = null;
			}

			/* Clean up before exiting */
		exit_create_index:
			if (pIndex != null)
			{
				//sqlite3DbFree(db, ref pIndex.zColAff );
				sqlite3DbFree(db, ref pIndex);
			}
			sqlite3ExprListDelete(db, ref pList);
			sqlite3SrcListDelete(db, ref pTblName);
			sqlite3DbFree(db, ref zName);
			return pRet;
		}

		/*
		** Fill the Index.aiRowEst[] array with default information - information
		** to be used when we have not run the ANALYZE command.
		**
		** aiRowEst[0] is suppose to contain the number of elements in the index.
		** Since we do not know, guess 1 million.  aiRowEst[1] is an estimate of the
		** number of rows in the table that match any particular value of the
		** first column of the index.  aiRowEst[2] is an estimate of the number
		** of rows that match any particular combiniation of the first 2 columns
		** of the index.  And so forth.  It must always be the case that
		*
		**           aiRowEst[N]<=aiRowEst[N-1]
		**           aiRowEst[N]>=1
		**
		** Apart from that, we have little to go on besides intuition as to
		** how aiRowEst[] should be initialized.  The numbers generated here
		** are based on typical values found in actual indices.
		*/

		private static void sqlite3DefaultRowEst(Index pIdx)
		{
			int[] a = pIdx.aiRowEst;
			int i;
			int n;
			Debug.Assert(a != null);
			a[0] = (int)pIdx.pTable.nRowEst;
			if (a[0] < 10)
				a[0] = 10;
			n = 10;
			for (i = 1; i <= pIdx.nColumn; i++)
			{
				a[i] = n;
				if (n > 5)
					n--;
			}
			if (pIdx.onError != OE_None)
			{
				a[pIdx.nColumn] = 1;
			}
		}

		/*
		** This routine will drop an existing named index.  This routine
		** implements the DROP INDEX statement.
		*/

		private static void sqlite3DropIndex(Parse pParse, SrcList pName, int ifExists)
		{
			Index pIndex;
			Vdbe v;
			sqlite3 db = pParse.db;
			int iDb;

			Debug.Assert(pParse.nErr == 0);   /* Never called with prior errors */
			//if ( db.mallocFailed != 0 )
			//{
			//  goto exit_drop_index;
			//}
			Debug.Assert(pName.nSrc == 1);
			if (SQLITE_OK != sqlite3ReadSchema(pParse))
			{
				goto exit_drop_index;
			}
			pIndex = sqlite3FindIndex(db, pName.a[0].zName, pName.a[0].zDatabase);
			if (pIndex == null)
			{
				if (ifExists == 0)
				{
					sqlite3ErrorMsg(pParse, "no such index: %S", pName, 0);
				}
				else
				{
					sqlite3CodeVerifyNamedSchema(pParse, pName.a[0].zDatabase);
				}
				pParse.checkSchema = 1;
				goto exit_drop_index;
			}
			if (pIndex.autoIndex != 0)
			{
				sqlite3ErrorMsg(pParse, "index associated with UNIQUE " +
				"or PRIMARY KEY constraint cannot be dropped", 0);
				goto exit_drop_index;
			}
			iDb = sqlite3SchemaToIndex(db, pIndex.pSchema);
#if !SQLITE_OMIT_AUTHORIZATION
{
int code = SQLITE_DROP_INDEX;
Table pTab = pIndex.pTable;
string zDb = db.aDb[iDb].zName;
string zTab = SCHEMA_TABLE(iDb);
if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){
goto exit_drop_index;
}
if( OMIT_TEMPDB ==0&& iDb ) code = SQLITE_DROP_TEMP_INDEX;
if( sqlite3AuthCheck(pParse, code, pIndex.zName, pTab.zName, zDb) ){
goto exit_drop_index;
}
}
#endif

			/* Generate code to remove the index and from the master table */
			v = sqlite3GetVdbe(pParse);
			if (v != null)
			{
				sqlite3BeginWriteOperation(pParse, 1, iDb);
				sqlite3NestedParse(pParse,
				"DELETE FROM %Q.%s WHERE name=%Q AND type='index'",
				db.aDb[iDb].zName, SCHEMA_TABLE(iDb),
				pIndex.zName
				);
				if (sqlite3FindTable(db, "sqlite_stat1", db.aDb[iDb].zName) != null)
				{
					sqlite3NestedParse(pParse,
					"DELETE FROM %Q.sqlite_stat1 WHERE idx=%Q",
					db.aDb[iDb].zName, pIndex.zName
					);
				}
				sqlite3ChangeCookie(pParse, iDb);
				destroyRootPage(pParse, pIndex.tnum, iDb);
				sqlite3VdbeAddOp4(v, OP_DropIndex, iDb, 0, 0, pIndex.zName, 0);
			}

		exit_drop_index:
			sqlite3SrcListDelete(db, ref pName);
		}

		/*
		** pArray is a pointer to an array of objects.  Each object in the
		** array is szEntry bytes in size.  This routine allocates a new
		** object on the end of the array.
		**
		** pnEntry is the number of entries already in use.  pnAlloc is
		** the previously allocated size of the array.  initSize is the
		** suggested initial array size allocation.
		**
		** The index of the new entry is returned in pIdx.
		**
		** This routine returns a pointer to the array of objects.  This
		** might be the same as the pArray parameter or it might be a different
		** pointer if the array was resized.
		*/

		private static T[] sqlite3ArrayAllocate<T>(
		sqlite3 db,       /* Connection to notify of malloc failures */
		T[] pArray,    /* Array of objects.  Might be reallocated */
		int szEntry,      /* Size of each object in the array */
		int initSize,     /* Suggested initial allocation, in elements */
		ref int pnEntry,      /* Number of objects currently in use */
		ref int pnAlloc,      /* Current size of the allocation, in elements */
		ref int pIdx      /* Write the index of a new slot here */
		) where T : new()
		{
			//char* z;
			if (pnEntry >= pnAlloc)
			{
				//void* pNew;
				int newSize;
				newSize = (pnAlloc) * 2 + initSize;
				//pNew = sqlite3DbRealloc(db, pArray, newSize * szEntry);
				//if (pNew == 0)
				//{
				//  pIdx = -1;
				//  return pArray;
				//}
				pnAlloc = newSize; //sqlite3DbMallocSize(db, pNew)/szEntry;
				//pArray = pNew;
				Array.Resize(ref pArray, newSize);
			}
			pArray[pnEntry] = new T();
			//z = (char)pArray;
			//memset(z[*pnEntry * szEntry], 0, szEntry);
			pIdx = pnEntry;
			++pnEntry;
			return pArray;
		}

		/*
		** Append a new element to the given IdList.  Create a new IdList if
		** need be.
		**
		** A new IdList is returned, or NULL if malloc() fails.
		*/

		// OVERLOADS, so I don't need to rewrite parse.c
		private static IdList sqlite3IdListAppend(sqlite3 db, int null_2, Token pToken)
		{
			return sqlite3IdListAppend(db, null, pToken);
		}

		private static IdList sqlite3IdListAppend(sqlite3 db, IdList pList, Token pToken)
		{
			int i = 0;
			if (pList == null)
			{
				pList = new IdList();//sqlite3DbMallocZero(db, sizeof(IdList));
				if (pList == null)
					return null;
				pList.nAlloc = 0;
			}
			pList.a = (IdList_item[])sqlite3ArrayAllocate(
			db,
			pList.a,
			-1,//sizeof(pList.a[0]),
			5,
			ref pList.nId,
			ref pList.nAlloc,
			ref i
			);
			if (i < 0)
			{
				sqlite3IdListDelete(db, ref pList);
				return null;
			}
			pList.a[i].zName = sqlite3NameFromToken(db, pToken);
			return pList;
		}

		/*
		** Delete an IdList.
		*/

		private static void sqlite3IdListDelete(sqlite3 db, ref IdList pList)
		{
			int i;
			if (pList == null)
				return;
			for (i = 0; i < pList.nId; i++)
			{
				sqlite3DbFree(db, ref pList.a[i].zName);
			}
			sqlite3DbFree(db, ref pList.a);
			sqlite3DbFree(db, ref pList);
		}

		/*
		** Return the index in pList of the identifier named zId.  Return -1
		** if not found.
		*/

		private static int sqlite3IdListIndex(IdList pList, string zName)
		{
			int i;
			if (pList == null)
				return -1;
			for (i = 0; i < pList.nId; i++)
			{
				if (pList.a[i].zName.Equals(zName, StringComparison.OrdinalIgnoreCase))
					return i;
			}
			return -1;
		}

		/*
		** Expand the space allocated for the given SrcList object by
		** creating nExtra new slots beginning at iStart.  iStart is zero based.
		** New slots are zeroed.
		**
		** For example, suppose a SrcList initially contains two entries: A,B.
		** To append 3 new entries onto the end, do this:
		**
		**    sqlite3SrcListEnlarge(db, pSrclist, 3, 2);
		**
		** After the call above it would contain:  A, B, nil, nil, nil.
		** If the iStart argument had been 1 instead of 2, then the result
		** would have been:  A, nil, nil, nil, B.  To prepend the new slots,
		** the iStart value would be 0.  The result then would
		** be: nil, nil, nil, A, B.
		**
		** If a memory allocation fails the SrcList is unchanged.  The
		** db.mallocFailed flag will be set to true.
		*/

		private static SrcList sqlite3SrcListEnlarge(
		sqlite3 db,       /* Database connection to notify of OOM errors */
		SrcList pSrc,     /* The SrcList to be enlarged */
		int nExtra,        /* Number of new slots to add to pSrc.a[] */
		int iStart         /* Index in pSrc.a[] of first new slot */
		)
		{
			int i;

			/* Sanity checking on calling parameters */
			Debug.Assert(iStart >= 0);
			Debug.Assert(nExtra >= 1);
			Debug.Assert(pSrc != null);
			Debug.Assert(iStart <= pSrc.nSrc);

			/* Allocate additional space if needed */
			if (pSrc.nSrc + nExtra > pSrc.nAlloc)
			{
				int nAlloc = pSrc.nSrc + nExtra;
				int nGot;
				// sqlite3DbRealloc(db, pSrc,
				//     sizeof(*pSrc) + (nAlloc-1)*sizeof(pSrc.a[0]) );
				pSrc.nAlloc = (i16)nAlloc;
				Array.Resize(ref pSrc.a, nAlloc);
				//    nGot = (sqlite3DbMallocSize(db, pNew) - sizeof(*pSrc))/sizeof(pSrc->a[0])+1;
				//pSrc->nAlloc = (u16)nGot;
			}

			/* Move existing slots that come after the newly inserted slots
			** out of the way */
			for (i = pSrc.nSrc - 1; i >= iStart; i--)
			{
				pSrc.a[i + nExtra] = pSrc.a[i];
			}
			pSrc.nSrc += (i16)nExtra;

			/* Zero the newly allocated slots */
			//memset(&pSrc.a[iStart], 0, sizeof(pSrc.a[0])*nExtra);
			for (i = iStart; i < iStart + nExtra; i++)
			{
				pSrc.a[i] = new SrcList_item();
				pSrc.a[i].iCursor = -1;
			}

			/* Return a pointer to the enlarged SrcList */
			return pSrc;
		}

		/*
		** Append a new table name to the given SrcList.  Create a new SrcList if
		** need be.  A new entry is created in the SrcList even if pTable is NULL.
		**
		** A SrcList is returned, or NULL if there is an OOM error.  The returned
		** SrcList might be the same as the SrcList that was input or it might be
		** a new one.  If an OOM error does occurs, then the prior value of pList
		** that is input to this routine is automatically freed.
		**
		** If pDatabase is not null, it means that the table has an optional
		** database name prefix.  Like this:  "database.table".  The pDatabase
		** points to the table name and the pTable points to the database name.
		** The SrcList.a[].zName field is filled with the table name which might
		** come from pTable (if pDatabase is NULL) or from pDatabase.
		** SrcList.a[].zDatabase is filled with the database name from pTable,
		** or with NULL if no database is specified.
		**
		** In other words, if call like this:
		**
		**         sqlite3SrcListAppend(D,A,B,0);
		**
		** Then B is a table name and the database name is unspecified.  If called
		** like this:
		**
		**         sqlite3SrcListAppend(D,A,B,C);
		**
		** Then C is the table name and B is the database name.  If C is defined
		** then so is B.  In other words, we never have a case where:
		**
		**         sqlite3SrcListAppend(D,A,0,C);
		**
		** Both pTable and pDatabase are assumed to be quoted.  They are dequoted
		** before being added to the SrcList.
		*/

		// OVERLOADS, so I don't need to rewrite parse.c
		private static SrcList sqlite3SrcListAppend(sqlite3 db, int null_2, Token pTable, int null_4)
		{
			return sqlite3SrcListAppend(db, null, pTable, null);
		}

		private static SrcList sqlite3SrcListAppend(sqlite3 db, int null_2, Token pTable, Token pDatabase)
		{
			return sqlite3SrcListAppend(db, null, pTable, pDatabase);
		}

		private static SrcList sqlite3SrcListAppend(
		sqlite3 db,        /* Connection to notify of malloc failures */
		SrcList pList,     /* Append to this SrcList. NULL creates a new SrcList */
		Token pTable,      /* Table to append */
		Token pDatabase    /* Database of the table */
		)
		{
			SrcList_item pItem;
			Debug.Assert(pDatabase == null || pTable != null);  /* Cannot have C without B */
			if (pList == null)
			{
				pList = new SrcList();//sqlite3DbMallocZero(db, SrcList.Length );
				//if ( pList == null ) return null;
				pList.nAlloc = 1;
				pList.a = new SrcList_item[1];
			}
			pList = sqlite3SrcListEnlarge(db, pList, 1, pList.nSrc);
			//if ( db.mallocFailed != 0 )
			//{
			//  sqlite3SrcListDelete( db, ref pList );
			//  return null;
			//}
			pItem = pList.a[pList.nSrc - 1];
			if (pDatabase != null && String.IsNullOrEmpty(pDatabase.z))
			{
				pDatabase = null;
			}
			if (pDatabase != null)
			{
				Token pTemp = pDatabase;
				pDatabase = pTable;
				pTable = pTemp;
			}
			pItem.zName = sqlite3NameFromToken(db, pTable);
			pItem.zDatabase = sqlite3NameFromToken(db, pDatabase);
			return pList;
		}

		/*
		** Assign VdbeCursor index numbers to all tables in a SrcList
		*/

		private static void sqlite3SrcListAssignCursors(Parse pParse, SrcList pList)
		{
			int i;
			SrcList_item pItem;
			Debug.Assert(pList != null /* || pParse.db.mallocFailed != 0 */ );
			if (pList != null)
			{
				for (i = 0; i < pList.nSrc; i++)
				{
					pItem = pList.a[i];
					if (pItem.iCursor >= 0)
						break;
					pItem.iCursor = pParse.nTab++;
					if (pItem.pSelect != null)
					{
						sqlite3SrcListAssignCursors(pParse, pItem.pSelect.pSrc);
					}
				}
			}
		}

		/*
		** Delete an entire SrcList including all its substructure.
		*/

		private static void sqlite3SrcListDelete(sqlite3 db, ref SrcList pList)
		{
			int i;
			SrcList_item pItem;
			if (pList == null)
				return;
			for (i = 0; i < pList.nSrc; i++)
			{//, pItem++){
				pItem = pList.a[i];
				sqlite3DbFree(db, ref pItem.zDatabase);
				sqlite3DbFree(db, ref pItem.zName);
				sqlite3DbFree(db, ref pItem.zAlias);
				sqlite3DbFree(db, ref pItem.zIndex);
				sqlite3DeleteTable(db, ref pItem.pTab);
				sqlite3SelectDelete(db, ref pItem.pSelect);
				sqlite3ExprDelete(db, ref pItem.pOn);
				sqlite3IdListDelete(db, ref pItem.pUsing);
			}
			sqlite3DbFree(db, ref pList);
		}

		/*
		** This routine is called by the parser to add a new term to the
		** end of a growing FROM clause.  The "p" parameter is the part of
		** the FROM clause that has already been constructed.  "p" is NULL
		** if this is the first term of the FROM clause.  pTable and pDatabase
		** are the name of the table and database named in the FROM clause term.
		** pDatabase is NULL if the database name qualifier is missing - the
		** usual case.  If the term has a alias, then pAlias points to the
		** alias token.  If the term is a subquery, then pSubquery is the
		** SELECT statement that the subquery encodes.  The pTable and
		** pDatabase parameters are NULL for subqueries.  The pOn and pUsing
		** parameters are the content of the ON and USING clauses.
		**
		** Return a new SrcList which encodes is the FROM with the new
		** term added.
		*/

		// OVERLOADS, so I don't need to rewrite parse.c
		private static SrcList sqlite3SrcListAppendFromTerm(Parse pParse, SrcList p, int null_3, int null_4, Token pAlias, Select pSubquery, Expr pOn, IdList pUsing)
		{
			return sqlite3SrcListAppendFromTerm(pParse, p, null, null, pAlias, pSubquery, pOn, pUsing);
		}

		private static SrcList sqlite3SrcListAppendFromTerm(Parse pParse, SrcList p, Token pTable, Token pDatabase, Token pAlias, int null_6, Expr pOn, IdList pUsing)
		{
			return sqlite3SrcListAppendFromTerm(pParse, p, pTable, pDatabase, pAlias, null, pOn, pUsing);
		}

		private static SrcList sqlite3SrcListAppendFromTerm(
		Parse pParse,          /* Parsing context */
		SrcList p,             /* The left part of the FROM clause already seen */
		Token pTable,          /* Name of the table to add to the FROM clause */
		Token pDatabase,       /* Name of the database containing pTable */
		Token pAlias,          /* The right-hand side of the AS subexpression */
		Select pSubquery,      /* A subquery used in place of a table name */
		Expr pOn,              /* The ON clause of a join */
		IdList pUsing          /* The USING clause of a join */
		)
		{
			SrcList_item pItem;
			sqlite3 db = pParse.db;
			if (null == p && (pOn != null || pUsing != null))
			{
				sqlite3ErrorMsg(pParse, "a JOIN clause is required before %s",
				  (pOn != null ? "ON" : "USING")
				);
				goto append_from_error;
			}
			p = sqlite3SrcListAppend(db, p, pTable, pDatabase);
			//if ( p == null || NEVER( p.nSrc == 0 ) )
			//{
			//  goto append_from_error;
			//}
			pItem = p.a[p.nSrc - 1];
			Debug.Assert(pAlias != null);
			if (pAlias.n != 0)
			{
				pItem.zAlias = sqlite3NameFromToken(db, pAlias);
			}
			pItem.pSelect = pSubquery;
			pItem.pOn = pOn;
			pItem.pUsing = pUsing;
			return p;
		append_from_error:
			Debug.Assert(p == null);
			sqlite3ExprDelete(db, ref pOn);
			sqlite3IdListDelete(db, ref pUsing);
			sqlite3SelectDelete(db, ref pSubquery);
			return null;
		}

		/*
		** Add an INDEXED BY or NOT INDEXED clause to the most recently added
		** element of the source-list passed as the second argument.
		*/

		private static void sqlite3SrcListIndexedBy(Parse pParse, SrcList p, Token pIndexedBy)
		{
			Debug.Assert(pIndexedBy != null);
			if (p != null && ALWAYS(p.nSrc > 0))
			{
				SrcList_item pItem = p.a[p.nSrc - 1];
				Debug.Assert(0 == pItem.notIndexed && pItem.zIndex == null);
				if (pIndexedBy.n == 1 && null == pIndexedBy.z)
				{
					/* A "NOT INDEXED" clause was supplied. See parse.y
					** construct "indexed_opt" for details. */
					pItem.notIndexed = 1;
				}
				else
				{
					pItem.zIndex = sqlite3NameFromToken(pParse.db, pIndexedBy);
				}
			}
		}

		/*
		** When building up a FROM clause in the parser, the join operator
		** is initially attached to the left operand.  But the code generator
		** expects the join operator to be on the right operand.  This routine
		** Shifts all join operators from left to right for an entire FROM
		** clause.
		**
		** Example: Suppose the join is like this:
		**
		**           A natural cross join B
		**
		** The operator is "natural cross join".  The A and B operands are stored
		** in p.a[0] and p.a[1], respectively.  The parser initially stores the
		** operator with A.  This routine shifts that operator over to B.
		*/

		private static void sqlite3SrcListShiftJoinType(SrcList p)
		{
			if (p != null && p.a != null)
			{
				int i;
				for (i = p.nSrc - 1; i > 0; i--)
				{
					p.a[i].jointype = p.a[i - 1].jointype;
				}
				p.a[0].jointype = 0;
			}
		}

		/*
		** Begin a transaction
		*/

		private static void sqlite3BeginTransaction(Parse pParse, int type)
		{
			sqlite3 db;
			Vdbe v;
			int i;

			Debug.Assert(pParse != null);
			db = pParse.db;
			Debug.Assert(db != null);
			/*  if( db.aDb[0].pBt==0 ) return; */
			if (sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "BEGIN", null, null) != 0)
			{
				return;
			}
			v = sqlite3GetVdbe(pParse);
			if (v == null)
				return;
			if (type != TK_DEFERRED)
			{
				for (i = 0; i < db.nDb; i++)
				{
					sqlite3VdbeAddOp2(v, OP_Transaction, i, (type == TK_EXCLUSIVE) ? 2 : 1);
					sqlite3VdbeUsesBtree(v, i);
				}
			}
			sqlite3VdbeAddOp2(v, OP_AutoCommit, 0, 0);
		}

		/*
		** Commit a transaction
		*/

		private static void sqlite3CommitTransaction(Parse pParse)
		{
			sqlite3 db;
			Vdbe v;

			Debug.Assert(pParse != null);
			db = pParse.db;
			Debug.Assert(db != null);
			/*  if( db.aDb[0].pBt==0 ) return; */
			if (sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "COMMIT", null, null) != 0)
			{
				return;
			}
			v = sqlite3GetVdbe(pParse);
			if (v != null)
			{
				sqlite3VdbeAddOp2(v, OP_AutoCommit, 1, 0);
			}
		}

		/*
		** Rollback a transaction
		*/

		private static void sqlite3RollbackTransaction(Parse pParse)
		{
			sqlite3 db;
			Vdbe v;

			Debug.Assert(pParse != null);
			db = pParse.db;
			Debug.Assert(db != null);
			/*  if( db.aDb[0].pBt==0 ) return; */
			if (sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "ROLLBACK", null, null) != 0)
			{
				return;
			}
			v = sqlite3GetVdbe(pParse);
			if (v != null)
			{
				sqlite3VdbeAddOp2(v, OP_AutoCommit, 1, 1);
			}
		}

		/*
		** This function is called by the parser when it parses a command to create,
		** release or rollback an SQL savepoint.
		*/
#if !SQLITE_OMIT_AUTHORIZATION
const string[] az = { "BEGIN", "RELEASE", "ROLLBACK" };
#endif

		private static void sqlite3Savepoint(Parse pParse, int op, Token pName)
		{
			string zName = sqlite3NameFromToken(pParse.db, pName);
			if (zName != null)
			{
				Vdbe v = sqlite3GetVdbe(pParse);
#if !SQLITE_OMIT_AUTHORIZATION
Debug.Assert( !SAVEPOINT_BEGIN && SAVEPOINT_RELEASE==1 && SAVEPOINT_ROLLBACK==2 );
#endif
				if (null == v
#if !SQLITE_OMIT_AUTHORIZATION
|| sqlite3AuthCheck(pParse, SQLITE_SAVEPOINT, az[op], zName, 0)
#endif
)
				{
					sqlite3DbFree(pParse.db, ref zName);
					return;
				}
				sqlite3VdbeAddOp4(v, OP_Savepoint, op, 0, 0, zName, P4_DYNAMIC);
			}
		}

		/*
		** Make sure the TEMP database is open and available for use.  Return
		** the number of errors.  Leave any error messages in the pParse structure.
		*/

		private static int sqlite3OpenTempDatabase(Parse pParse)
		{
			sqlite3 db = pParse.db;
			if (db.aDb[1].pBt == null && pParse.explain == 0)
			{
				int rc;
				Btree pBt = null;
				const int flags =
				SQLITE_OPEN_READWRITE |
				SQLITE_OPEN_CREATE |
				SQLITE_OPEN_EXCLUSIVE |
				SQLITE_OPEN_DELETEONCLOSE |
				SQLITE_OPEN_TEMP_DB;

				rc = sqlite3BtreeOpen(db.pVfs, null, db, ref pBt, 0, flags);
				if (rc != SQLITE_OK)
				{
					sqlite3ErrorMsg(pParse, "unable to open a temporary database " +
					"file for storing temporary tables");
					pParse.rc = rc;
					return 1;
				}
				db.aDb[1].pBt = pBt;
				Debug.Assert(db.aDb[1].pSchema != null);
				if (SQLITE_NOMEM == sqlite3BtreeSetPageSize(pBt, db.nextPagesize, -1, 0))
				{
					//  db.mallocFailed = 1;
				}
			}
			return 0;
		}

		/*
		** Generate VDBE code that will verify the schema cookie and start
		** a read-transaction for all named database files.
		**
		** It is important that all schema cookies be verified and all
		** read transactions be started before anything else happens in
		** the VDBE program.  But this routine can be called after much other
		** code has been generated.  So here is what we do:
		**
		** The first time this routine is called, we code an OP_Goto that
		** will jump to a subroutine at the end of the program.  Then we
		** record every database that needs its schema verified in the
		** pParse.cookieMask field.  Later, after all other code has been
		** generated, the subroutine that does the cookie verifications and
		** starts the transactions will be coded and the OP_Goto P2 value
		** will be made to point to that subroutine.  The generation of the
		** cookie verification subroutine code happens in sqlite3FinishCoding().
		**
		** If iDb<0 then code the OP_Goto only - don't set flag to verify the
		** schema on any databases.  This can be used to position the OP_Goto
		** early in the code, before we know if any database tables will be used.
		*/

		private static void sqlite3CodeVerifySchema(Parse pParse, int iDb)
		{
			Parse pToplevel = sqlite3ParseToplevel(pParse);

			if (pToplevel.cookieGoto == 0)
			{
				Vdbe v = sqlite3GetVdbe(pToplevel);
				if (v == null)
					return;  /* This only happens if there was a prior error */
				pToplevel.cookieGoto = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0) + 1;
			}
			if (iDb >= 0)
			{
				sqlite3 db = pToplevel.db;
				yDbMask mask;
				Debug.Assert(iDb < db.nDb);
				Debug.Assert(db.aDb[iDb].pBt != null || iDb == 1);
				Debug.Assert(iDb < SQLITE_MAX_ATTACHED + 2);
				Debug.Assert(sqlite3SchemaMutexHeld(db, iDb, null));
				mask = ((yDbMask)1) << iDb;
				if ((pToplevel.cookieMask & mask) == 0)
				{
					pToplevel.cookieMask |= mask;
					pToplevel.cookieValue[iDb] = db.aDb[iDb].pSchema.schema_cookie;
					if (0 == OMIT_TEMPDB && iDb == 1)
					{
						sqlite3OpenTempDatabase(pToplevel);
					}
				}
			}
		}

		/*
		** If argument zDb is NULL, then call sqlite3CodeVerifySchema() for each
		** attached database. Otherwise, invoke it for the database named zDb only.
		*/

		private static void sqlite3CodeVerifyNamedSchema(Parse pParse, string zDb)
		{
			sqlite3 db = pParse.db;
			int i;
			for (i = 0; i < db.nDb; i++)
			{
				Db pDb = db.aDb[i];
				if (pDb.pBt != null && (null == zDb || 0 == zDb.CompareTo(pDb.zName)))
				{
					sqlite3CodeVerifySchema(pParse, i);
				}
			}
		}

		/*
		** Generate VDBE code that prepares for doing an operation that
		** might change the database.
		**
		** This routine starts a new transaction if we are not already within
		** a transaction.  If we are already within a transaction, then a checkpoint
		** is set if the setStatement parameter is true.  A checkpoint should
		** be set for operations that might fail (due to a constraint) part of
		** the way through and which will need to undo some writes without having to
		** rollback the whole transaction.  For operations where all constraints
		** can be checked before any changes are made to the database, it is never
		** necessary to undo a write and the checkpoint should not be set.
		*/

		private static void sqlite3BeginWriteOperation(Parse pParse, int setStatement, int iDb)
		{
			Parse pToplevel = sqlite3ParseToplevel(pParse);
			sqlite3CodeVerifySchema(pParse, iDb);
			pToplevel.writeMask |= ((yDbMask)1) << iDb;
			pToplevel.isMultiWrite |= (u8)setStatement;
		}

		/*
		** Indicate that the statement currently under construction might write
		** more than one entry (example: deleting one row then inserting another,
		** inserting multiple rows in a table, or inserting a row and index entries.)
		** If an abort occurs after some of these writes have completed, then it will
		** be necessary to undo the completed writes.
		*/

		private static void sqlite3MultiWrite(Parse pParse)
		{
			Parse pToplevel = sqlite3ParseToplevel(pParse);
			pToplevel.isMultiWrite = 1;
		}

		/*
		** The code generator calls this routine if is discovers that it is
		** possible to abort a statement prior to completion.  In order to
		** perform this abort without corrupting the database, we need to make
		** sure that the statement is protected by a statement transaction.
		**
		** Technically, we only need to set the mayAbort flag if the
		** isMultiWrite flag was previously set.  There is a time dependency
		** such that the abort must occur after the multiwrite.  This makes
		** some statements involving the REPLACE conflict resolution algorithm
		** go a little faster.  But taking advantage of this time dependency
		** makes it more difficult to prove that the code is correct (in
		** particular, it prevents us from writing an effective
		** implementation of sqlite3AssertMayAbort()) and so we have chosen
		** to take the safe route and skip the optimization.
		*/

		private static void sqlite3MayAbort(Parse pParse)
		{
			Parse pToplevel = sqlite3ParseToplevel(pParse);
			pToplevel.mayAbort = 1;
		}

		/*
		** Code an OP_Halt that causes the vdbe to return an SQLITE_CONSTRAINT
		** error. The onError parameter determines which (if any) of the statement
		** and/or current transaction is rolled back.
		*/

		private static void sqlite3HaltConstraint(Parse pParse, int onError, string p4, int p4type)
		{
			Vdbe v = sqlite3GetVdbe(pParse);
			if (onError == OE_Abort)
			{
				sqlite3MayAbort(pParse);
			}
			sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, onError, 0, p4, p4type);
		}

		private static void sqlite3HaltConstraint(Parse pParse, int onError, byte[] p4, int p4type)
		{
			Vdbe v = sqlite3GetVdbe(pParse);
			if (onError == OE_Abort)
			{
				sqlite3MayAbort(pParse);
			}
			sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, onError, 0, p4, p4type);
		}

		/*
		** Check to see if pIndex uses the collating sequence pColl.  Return
		** true if it does and false if it does not.
		*/
#if !SQLITE_OMIT_REINDEX

		private static bool collationMatch(string zColl, Index pIndex)
		{
			int i;
			Debug.Assert(zColl != null);
			for (i = 0; i < pIndex.nColumn; i++)
			{
				string z = pIndex.azColl[i];
				Debug.Assert(z != null);
				if (z.Equals(zColl, StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
			}
			return false;
		}

#endif

		/*
** Recompute all indices of pTab that use the collating sequence pColl.
** If pColl == null then recompute all indices of pTab.
*/
#if !SQLITE_OMIT_REINDEX

		private static void reindexTable(Parse pParse, Table pTab, string zColl)
		{
			Index pIndex;              /* An index associated with pTab */

			for (pIndex = pTab.pIndex; pIndex != null; pIndex = pIndex.pNext)
			{
				if (zColl == null || collationMatch(zColl, pIndex))
				{
					int iDb = sqlite3SchemaToIndex(pParse.db, pTab.pSchema);
					sqlite3BeginWriteOperation(pParse, 0, iDb);
					sqlite3RefillIndex(pParse, pIndex, -1);
				}
			}
		}

#endif

		/*
** Recompute all indices of all tables in all databases where the
** indices use the collating sequence pColl.  If pColl == null then recompute
** all indices everywhere.
*/
#if !SQLITE_OMIT_REINDEX

		private static void reindexDatabases(Parse pParse, string zColl)
		{
			Db pDb;                    /* A single database */
			int iDb;                   /* The database index number */
			sqlite3 db = pParse.db;    /* The database connection */
			HashElem k;                /* For looping over tables in pDb */
			Table pTab;                /* A table in the database */

			Debug.Assert(sqlite3BtreeHoldsAllMutexes(db));  /* Needed for schema access */
			for (iDb = 0; iDb < db.nDb; iDb++)//, pDb++ )
			{
				pDb = db.aDb[iDb];
				Debug.Assert(pDb != null);
				for (k = pDb.pSchema.tblHash.first; k != null; k = k.next) //for ( k = sqliteHashFirst( pDb.pSchema.tblHash ) ; k != null ; k = sqliteHashNext( k ) )
				{
					pTab = (Table)k.data;// sqliteHashData( k );
					reindexTable(pParse, pTab, zColl);
				}
			}
		}

#endif

		/*
** Generate code for the REINDEX command.
**
**        REINDEX                            -- 1
**        REINDEX  <collation>               -- 2
**        REINDEX  ?<database>.?<tablename>  -- 3
**        REINDEX  ?<database>.?<indexname>  -- 4
**
** Form 1 causes all indices in all attached databases to be rebuilt.
** Form 2 rebuilds all indices in all databases that use the named
** collating function.  Forms 3 and 4 rebuild the named index or all
** indices associated with the named table.
*/
#if !SQLITE_OMIT_REINDEX

		// OVERLOADS, so I don't need to rewrite parse.c
		private static void sqlite3Reindex(Parse pParse, int null_2, int null_3)
		{
			sqlite3Reindex(pParse, null, null);
		}

		private static void sqlite3Reindex(Parse pParse, Token pName1, Token pName2)
		{
			CollSeq pColl;                /* Collating sequence to be reindexed, or NULL */
			string z;                     /* Name of a table or index */
			string zDb;                   /* Name of the database */
			Table pTab;                   /* A table in the database */
			Index pIndex;                 /* An index associated with pTab */
			int iDb;                      /* The database index number */
			sqlite3 db = pParse.db;       /* The database connection */
			Token pObjName = new Token();  /* Name of the table or index to be reindexed */

			/* Read the database schema. If an error occurs, leave an error message
			** and code in pParse and return NULL. */
			if (SQLITE_OK != sqlite3ReadSchema(pParse))
			{
				return;
			}

			if (pName1 == null)
			{
				reindexDatabases(pParse, null);
				return;
			}
			else if (NEVER(pName2 == null) || pName2.z == null || pName2.z.Length == 0)
			{
				string zColl;
				Debug.Assert(pName1.z != null);
				zColl = sqlite3NameFromToken(pParse.db, pName1);
				if (zColl == null)
					return;
				pColl = sqlite3FindCollSeq(db, ENC(db), zColl, 0);
				if (pColl != null)
				{
					reindexDatabases(pParse, zColl);
					sqlite3DbFree(db, ref zColl);
					return;
				}
				sqlite3DbFree(db, ref zColl);
			}
			iDb = sqlite3TwoPartName(pParse, pName1, pName2, ref pObjName);
			if (iDb < 0)
				return;
			z = sqlite3NameFromToken(db, pObjName);
			if (z == null)
				return;
			zDb = db.aDb[iDb].zName;
			pTab = sqlite3FindTable(db, z, zDb);
			if (pTab != null)
			{
				reindexTable(pParse, pTab, null);
				sqlite3DbFree(db, ref z);
				return;
			}
			pIndex = sqlite3FindIndex(db, z, zDb);
			sqlite3DbFree(db, ref z);
			if (pIndex != null)
			{
				sqlite3BeginWriteOperation(pParse, 0, iDb);
				sqlite3RefillIndex(pParse, pIndex, -1);
				return;
			}
			sqlite3ErrorMsg(pParse, "unable to identify the object to be reindexed");
		}

#endif

		/*
** Return a dynamicly allocated KeyInfo structure that can be used
** with OP_OpenRead or OP_OpenWrite to access database index pIdx.
**
** If successful, a pointer to the new structure is returned. In this case
** the caller is responsible for calling sqlite3DbFree(db, ) on the returned
** pointer. If an error occurs (out of memory or missing collation
** sequence), NULL is returned and the state of pParse updated to reflect
** the error.
*/

		private static KeyInfo sqlite3IndexKeyinfo(Parse pParse, Index pIdx)
		{
			int i;
			int nCol = pIdx.nColumn;
			//int nBytes = KeyInfo.Length + (nCol - 1) * CollSeq*.Length + nCol;
			sqlite3 db = pParse.db;
			KeyInfo pKey = new KeyInfo();// (KeyInfo)sqlite3DbMallocZero(db, nBytes);

			if (pKey != null)
			{
				pKey.db = pParse.db;
				pKey.aSortOrder = new byte[nCol];
				pKey.aColl = new CollSeq[nCol];// (u8)&(pKey.aColl[nCol]);
				//        Debug.Assert(pKey.aSortOrder[nCol] == (((u8)pKey)[nBytes]));
				for (i = 0; i < nCol; i++)
				{
					string zColl = pIdx.azColl[i];
					Debug.Assert(zColl != null);
					pKey.aColl[i] = sqlite3LocateCollSeq(pParse, zColl);
					pKey.aSortOrder[i] = pIdx.aSortOrder[i];
				}
				pKey.nField = (u16)nCol;
			}

			if (pParse.nErr != 0)
			{
				pKey = null;
				sqlite3DbFree(db, ref pKey);
			}
			return pKey;
		}
	}
}